diff --git a/.github/workflows/build_aarch64.yml b/.github/workflows/build_aarch64.yml
index 40fc8d63a..e5dbf9494 100644
--- a/.github/workflows/build_aarch64.yml
+++ b/.github/workflows/build_aarch64.yml
@@ -36,7 +36,7 @@ jobs:
         - 'broadcom_64bit'
     steps:
     - name: Setup Python
-      uses: actions/setup-python@v4
+      uses: actions/setup-python@v5
       with:
         python-version: '3.x'
 
diff --git a/.github/workflows/build_arm.yml b/.github/workflows/build_arm.yml
index 47dea1925..9f3270c91 100644
--- a/.github/workflows/build_arm.yml
+++ b/.github/workflows/build_arm.yml
@@ -37,7 +37,7 @@ jobs:
         # Alphabetical order
         - 'broadcom_32bit'
         - 'kinetis_k32l2'
-        - 'lpc11 lpc13 lpc15 lpc17'
+        - 'lpc11 lpc13 lpc15'
         - 'lpc51'
         - 'mm32 msp432e4'
         - 'samd11 same5x saml2x'
@@ -46,7 +46,7 @@ jobs:
         - 'tm4c123 xmc4000'
     steps:
     - name: Setup Python
-      uses: actions/setup-python@v4
+      uses: actions/setup-python@v5
       with:
         python-version: '3.x'
 
diff --git a/.github/workflows/build_esp.yml b/.github/workflows/build_esp.yml
index 33caa0edb..315df255e 100644
--- a/.github/workflows/build_esp.yml
+++ b/.github/workflows/build_esp.yml
@@ -8,6 +8,7 @@ on:
       - 'examples/**'
       - 'lib/**'
       - 'hw/**'
+      - 'test/hil/**'
       - '.github/workflows/build_esp.yml'
   pull_request:
     branches: [ master ]
@@ -16,6 +17,7 @@ on:
       - 'examples/**'
       - 'lib/**'
       - 'hw/**'
+      - 'test/hil/**'
       - '.github/workflows/build_esp.yml'
 
 concurrency:
@@ -35,7 +37,7 @@ jobs:
         - 'espressif_s3_devkitc'
     steps:
     - name: Setup Python
-      uses: actions/setup-python@v4
+      uses: actions/setup-python@v5
       with:
         python-version: '3.x'
 
diff --git a/.github/workflows/build_iar.yml b/.github/workflows/build_iar.yml
index 9f2cc0351..a0a022ecc 100644
--- a/.github/workflows/build_iar.yml
+++ b/.github/workflows/build_iar.yml
@@ -9,6 +9,7 @@ on:
       - 'lib/**'
       - 'hw/**'
       - 'tools/get_deps.py'
+      - 'test/hil/**'
       - '.github/workflows/build_iar.yml'
   pull_request:
     branches: [ master ]
@@ -18,6 +19,7 @@ on:
       - 'lib/**'
       - 'hw/**'
       - 'tools/get_deps.py'
+      - 'test/hil/**'
       - '.github/workflows/build_iar.yml'
 
 concurrency:
diff --git a/.github/workflows/build_msp430.yml b/.github/workflows/build_msp430.yml
index 296dbb766..f913df913 100644
--- a/.github/workflows/build_msp430.yml
+++ b/.github/workflows/build_msp430.yml
@@ -36,7 +36,7 @@ jobs:
 
     steps:
     - name: Setup Python
-      uses: actions/setup-python@v4
+      uses: actions/setup-python@v5
       with:
         python-version: '3.x'
 
diff --git a/.github/workflows/build_renesas.yml b/.github/workflows/build_renesas.yml
index 017a29eee..ec06c9426 100644
--- a/.github/workflows/build_renesas.yml
+++ b/.github/workflows/build_renesas.yml
@@ -35,7 +35,7 @@ jobs:
         - 'rx'
     steps:
     - name: Setup Python
-      uses: actions/setup-python@v4
+      uses: actions/setup-python@v5
       with:
         python-version: '3.x'
 
diff --git a/.github/workflows/build_riscv.yml b/.github/workflows/build_riscv.yml
index e030bb77f..e891a3a51 100644
--- a/.github/workflows/build_riscv.yml
+++ b/.github/workflows/build_riscv.yml
@@ -37,7 +37,7 @@ jobs:
         - 'gd32vf103'
     steps:
     - name: Setup Python
-      uses: actions/setup-python@v4
+      uses: actions/setup-python@v5
       with:
         python-version: '3.x'
 
diff --git a/.github/workflows/build_win_mac.yml b/.github/workflows/build_win_mac.yml
index 7fa1a6943..b33b5b593 100644
--- a/.github/workflows/build_win_mac.yml
+++ b/.github/workflows/build_win_mac.yml
@@ -35,7 +35,7 @@ jobs:
 
     steps:
     - name: Setup Python
-      uses: actions/setup-python@v4
+      uses: actions/setup-python@v5
       with:
         python-version: '3.x'
 
diff --git a/.github/workflows/cmake_arm.yml b/.github/workflows/cmake_arm.yml
index 13120f96f..f97645ad1 100644
--- a/.github/workflows/cmake_arm.yml
+++ b/.github/workflows/cmake_arm.yml
@@ -8,6 +8,7 @@ on:
       - 'examples/**'
       - 'lib/**'
       - 'hw/**'
+      - 'test/hil/**'
       - 'tools/get_deps.py'
       - '.github/workflows/cmake_arm.yml'
   pull_request:
@@ -17,6 +18,7 @@ on:
       - 'examples/**'
       - 'lib/**'
       - 'hw/**'
+      - 'test/hil/**'
       - 'tools/get_deps.py'
       - '.github/workflows/cmake_arm.yml'
 
@@ -37,7 +39,7 @@ jobs:
           # Alphabetical order
           - 'imxrt'
           - 'kinetis_kl'
-          - 'lpc18 lpc40 lpc43'
+          - 'lpc17 lpc18 lpc40 lpc43'
           - 'lpc54 lpc55'
           - 'mcx'
           - 'nrf'
@@ -57,7 +59,7 @@ jobs:
           - 'stm32u5'
     steps:
     - name: Setup Python
-      uses: actions/setup-python@v4
+      uses: actions/setup-python@v5
       with:
         python-version: '3.x'
 
diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml
index 2e9ae9264..e36259daa 100644
--- a/.github/workflows/pre-commit.yml
+++ b/.github/workflows/pre-commit.yml
@@ -15,7 +15,7 @@ jobs:
     runs-on: ubuntu-latest
     steps:
     - name: Setup Python
-      uses: actions/setup-python@v4
+      uses: actions/setup-python@v5
       with:
         python-version: '3.x'
 
diff --git a/.idea/cmake.xml b/.idea/cmake.xml
index d3917c1f7..3cb883e62 100644
--- a/.idea/cmake.xml
+++ b/.idea/cmake.xml
@@ -80,6 +80,7 @@
       
       
       
+      
     
   
 
\ No newline at end of file
diff --git a/examples/device/video_capture/src/images.h b/examples/device/video_capture/src/images.h
index 784e7754c..ac372cb16 100644
--- a/examples/device/video_capture/src/images.h
+++ b/examples/device/video_capture/src/images.h
@@ -1,4 +1,5 @@
 #if defined(CFG_EXAMPLE_VIDEO_DISABLE_MJPEG)
+// uncopmressed frame
 static const unsigned char frame_buffer[128 * (96 + 1) * 2] = {
   /* 0 */
   0xeb, 0x80, 0xeb, 0x80, 0xeb, 0x80, 0xeb, 0x80, 0xeb, 0x80, 0xeb, 0x80, 0xeb, 0x80, 0xeb, 0x80,
@@ -1650,8 +1651,10 @@ static const unsigned char frame_buffer[128 * (96 + 1) * 2] = {
   0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80,
   0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80,
 };
+
 #else
 
+// mpeg compressed data (not CFG_EXAMPLE_VIDEO_DISABLE_MJPEG)
 #define color_bar_0_jpg_len  511
 #define color_bar_1_jpg_len  512
 #define color_bar_2_jpg_len  511
diff --git a/examples/device/video_capture/src/main.c b/examples/device/video_capture/src/main.c
index 711d4710a..e63335e67 100644
--- a/examples/device/video_capture/src/main.c
+++ b/examples/device/video_capture/src/main.c
@@ -40,7 +40,7 @@
  * - 1000 ms : device mounted
  * - 2500 ms : device is suspended
  */
-enum  {
+enum {
   BLINK_NOT_MOUNTED = 250,
   BLINK_MOUNTED = 1000,
   BLINK_SUSPENDED = 2500,
@@ -52,8 +52,7 @@ void led_blinking_task(void);
 void video_task(void);
 
 /*------------- MAIN -------------*/
-int main(void)
-{
+int main(void) {
   board_init();
 
   // init device stack on configured roothub port
@@ -63,8 +62,7 @@ int main(void)
     board_init_after_tusb();
   }
 
-  while (1)
-  {
+  while (1) {
     tud_task(); // tinyusb device task
     led_blinking_task();
 
@@ -77,33 +75,28 @@ int main(void)
 //--------------------------------------------------------------------+
 
 // Invoked when device is mounted
-void tud_mount_cb(void)
-{
+void tud_mount_cb(void) {
   blink_interval_ms = BLINK_MOUNTED;
 }
 
 // Invoked when device is unmounted
-void tud_umount_cb(void)
-{
+void tud_umount_cb(void) {
   blink_interval_ms = BLINK_NOT_MOUNTED;
 }
 
 // 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;
   blink_interval_ms = BLINK_SUSPENDED;
 }
 
 // Invoked when usb bus is resumed
-void tud_resume_cb(void)
-{
+void tud_resume_cb(void) {
   blink_interval_ms = tud_mounted() ? BLINK_MOUNTED : BLINK_NOT_MOUNTED;
 }
 
-
 //--------------------------------------------------------------------+
 // USB Video
 //--------------------------------------------------------------------+
@@ -111,11 +104,12 @@ static unsigned frame_num = 0;
 static unsigned tx_busy = 0;
 static unsigned interval_ms = 1000 / FRAME_RATE;
 
-/* YUY2 frame buffer */
 #ifdef CFG_EXAMPLE_VIDEO_READONLY
+// For mcus that does not have enough SRAM for frame buffer, we use fixed frame data.
+// To further reduce the size, we use MJPEG format instead of YUY2.
 #include "images.h"
 
-# if !defined(CFG_EXAMPLE_VIDEO_DISABLE_MJPEG)
+#if !defined(CFG_EXAMPLE_VIDEO_DISABLE_MJPEG)
 static struct {
   uint32_t       size;
   uint8_t const *buffer;
@@ -129,29 +123,30 @@ static struct {
   {color_bar_6_jpg_len, color_bar_6_jpg},
   {color_bar_7_jpg_len, color_bar_7_jpg},
 };
-# endif
+#endif
 
 #else
+
+// YUY2 frame buffer
 static uint8_t frame_buffer[FRAME_WIDTH * FRAME_HEIGHT * 16 / 8];
-static void fill_color_bar(uint8_t *buffer, unsigned start_position)
-{
-  /* EBU color bars
-   * See also https://stackoverflow.com/questions/6939422 */
+
+static void fill_color_bar(uint8_t* buffer, unsigned start_position) {
+  /* EBU color bars: https://stackoverflow.com/questions/6939422 */
   static uint8_t const bar_color[8][4] = {
-    /*  Y,   U,   Y,   V */
-    { 235, 128, 235, 128}, /* 100% White */
-    { 219,  16, 219, 138}, /* Yellow */
-    { 188, 154, 188,  16}, /* Cyan */
-    { 173,  42, 173,  26}, /* Green */
-    {  78, 214,  78, 230}, /* Magenta */
-    {  63, 102,  63, 240}, /* Red */
-    {  32, 240,  32, 118}, /* Blue */
-    {  16, 128,  16, 128}, /* Black */
+      /*  Y,   U,   Y,   V */
+      { 235, 128, 235, 128}, /* 100% White */
+      { 219,  16, 219, 138}, /* Yellow */
+      { 188, 154, 188,  16}, /* Cyan */
+      { 173,  42, 173,  26}, /* Green */
+      {  78, 214,  78, 230}, /* Magenta */
+      {  63, 102,  63, 240}, /* Red */
+      {  32, 240,  32, 118}, /* Blue */
+      {  16, 128,  16, 128}, /* Black */
   };
-  uint8_t *p;
+  uint8_t* p;
 
   /* Generate the 1st line */
-  uint8_t *end = &buffer[FRAME_WIDTH * 2];
+  uint8_t* end = &buffer[FRAME_WIDTH * 2];
   unsigned idx = (FRAME_WIDTH / 2 - 1) - (start_position % (FRAME_WIDTH / 2));
   p = &buffer[idx * 4];
   for (unsigned i = 0; i < 8; ++i) {
@@ -163,6 +158,7 @@ static void fill_color_bar(uint8_t *buffer, unsigned start_position)
       }
     }
   }
+
   /* Duplicate the 1st line to the others */
   p = &buffer[FRAME_WIDTH * 2];
   for (unsigned i = 1; i < FRAME_HEIGHT; ++i) {
@@ -170,16 +166,16 @@ static void fill_color_bar(uint8_t *buffer, unsigned start_position)
     p += FRAME_WIDTH * 2;
   }
 }
+
 #endif
 
-void video_task(void)
-{
+void video_task(void) {
   static unsigned start_ms = 0;
   static unsigned already_sent = 0;
 
   if (!tud_video_n_streaming(0, 0)) {
-    already_sent  = 0;
-    frame_num     = 0;
+    already_sent = 0;
+    frame_num = 0;
     return;
   }
 
@@ -187,15 +183,15 @@ void video_task(void)
     already_sent = 1;
     start_ms = board_millis();
 #ifdef CFG_EXAMPLE_VIDEO_READONLY
-# if defined(CFG_EXAMPLE_VIDEO_DISABLE_MJPEG)
+    #if defined(CFG_EXAMPLE_VIDEO_DISABLE_MJPEG)
     tud_video_n_frame_xfer(0, 0, (void*)(uintptr_t)&frame_buffer[(frame_num % (FRAME_WIDTH / 2)) * 4],
                            FRAME_WIDTH * FRAME_HEIGHT * 16/8);
-# else
+    #else
     tud_video_n_frame_xfer(0, 0, (void*)(uintptr_t)frames[frame_num % 8].buffer, frames[frame_num % 8].size);
-# endif
+    #endif
 #else
     fill_color_bar(frame_buffer, frame_num);
-    tud_video_n_frame_xfer(0, 0, (void*)frame_buffer, FRAME_WIDTH * FRAME_HEIGHT * 16/8);
+    tud_video_n_frame_xfer(0, 0, (void*) frame_buffer, FRAME_WIDTH * FRAME_HEIGHT * 16 / 8);
 #endif
   }
 
@@ -205,30 +201,30 @@ void video_task(void)
   start_ms += interval_ms;
 
 #ifdef CFG_EXAMPLE_VIDEO_READONLY
-# if defined(CFG_EXAMPLE_VIDEO_DISABLE_MJPEG)
+  #if defined(CFG_EXAMPLE_VIDEO_DISABLE_MJPEG)
   tud_video_n_frame_xfer(0, 0, (void*)(uintptr_t)&frame_buffer[(frame_num % (FRAME_WIDTH / 2)) * 4],
                          FRAME_WIDTH * FRAME_HEIGHT * 16/8);
-# else
+  #else
   tud_video_n_frame_xfer(0, 0, (void*)(uintptr_t)frames[frame_num % 8].buffer, frames[frame_num % 8].size);
-# endif
+  #endif
 #else
   fill_color_bar(frame_buffer, frame_num);
-  tud_video_n_frame_xfer(0, 0, (void*)frame_buffer, FRAME_WIDTH * FRAME_HEIGHT * 16/8);
+  tud_video_n_frame_xfer(0, 0, (void*) frame_buffer, FRAME_WIDTH * FRAME_HEIGHT * 16 / 8);
 #endif
 }
 
-void tud_video_frame_xfer_complete_cb(uint_fast8_t ctl_idx, uint_fast8_t stm_idx)
-{
-  (void)ctl_idx; (void)stm_idx;
+void tud_video_frame_xfer_complete_cb(uint_fast8_t ctl_idx, uint_fast8_t stm_idx) {
+  (void) ctl_idx;
+  (void) stm_idx;
   tx_busy = 0;
   /* flip buffer */
   ++frame_num;
 }
 
 int tud_video_commit_cb(uint_fast8_t ctl_idx, uint_fast8_t stm_idx,
-			video_probe_and_commit_control_t const *parameters)
-{
-  (void)ctl_idx; (void)stm_idx;
+                        video_probe_and_commit_control_t const* parameters) {
+  (void) ctl_idx;
+  (void) stm_idx;
   /* convert unit to ms from 100 ns */
   interval_ms = parameters->dwFrameInterval / 10000;
   return VIDEO_ERROR_NONE;
@@ -237,13 +233,12 @@ int tud_video_commit_cb(uint_fast8_t ctl_idx, uint_fast8_t stm_idx,
 //--------------------------------------------------------------------+
 // BLINKING TASK
 //--------------------------------------------------------------------+
-void led_blinking_task(void)
-{
+void led_blinking_task(void) {
   static uint32_t start_ms = 0;
   static bool led_state = false;
 
   // Blink every interval ms
-  if ( board_millis() - start_ms < blink_interval_ms) return; // not enough time
+  if (board_millis() - start_ms < blink_interval_ms) return; // not enough time
   start_ms += blink_interval_ms;
 
   board_led_write(led_state);
diff --git a/examples/device/video_capture/src/tusb_config.h b/examples/device/video_capture/src/tusb_config.h
index 274bf2b9c..d563c6f5c 100644
--- a/examples/device/video_capture/src/tusb_config.h
+++ b/examples/device/video_capture/src/tusb_config.h
@@ -97,11 +97,11 @@
 // The number of video streaming interfaces
 #define CFG_TUD_VIDEO_STREAMING  1
 
-// video streaming endpoint size
+// video streaming endpoint buffer size
 #define CFG_TUD_VIDEO_STREAMING_EP_BUFSIZE  256
 
 // use bulk endpoint for streaming interface
-#define CFG_TUD_VIDEO_STREAMING_BULK 0
+#define CFG_TUD_VIDEO_STREAMING_BULK 1
 
 #ifdef __cplusplus
  }
diff --git a/examples/device/video_capture/src/usb_descriptors.c b/examples/device/video_capture/src/usb_descriptors.c
index 49919fc58..d38f158c8 100644
--- a/examples/device/video_capture/src/usb_descriptors.c
+++ b/examples/device/video_capture/src/usb_descriptors.c
@@ -43,8 +43,7 @@
 //--------------------------------------------------------------------+
 // Device Descriptors
 //--------------------------------------------------------------------+
-tusb_desc_device_t const desc_device =
-{
+tusb_desc_device_t const desc_device = {
     .bLength            = sizeof(tusb_desc_device_t),
     .bDescriptorType    = TUSB_DESC_DEVICE,
     .bcdUSB             = USB_BCD,
@@ -70,111 +69,70 @@ tusb_desc_device_t const desc_device =
 
 // Invoked when received GET DEVICE DESCRIPTOR
 // Application return pointer to descriptor
-uint8_t const * tud_descriptor_device_cb(void)
-{
-  return (uint8_t const *) &desc_device;
+uint8_t const* tud_descriptor_device_cb(void) {
+  return (uint8_t const*) &desc_device;
 }
 
 //--------------------------------------------------------------------+
 // Configuration Descriptor
 //--------------------------------------------------------------------+
 
-#if defined(CFG_EXAMPLE_VIDEO_READONLY) && !defined(CFG_EXAMPLE_VIDEO_DISABLE_MJPEG)
-# if 1 == CFG_TUD_VIDEO_STREAMING_BULK
-#  define CONFIG_TOTAL_LEN    (TUD_CONFIG_DESC_LEN + TUD_VIDEO_CAPTURE_DESC_MJPEG_BULK_LEN)
-# else
-#  define CONFIG_TOTAL_LEN    (TUD_CONFIG_DESC_LEN + TUD_VIDEO_CAPTURE_DESC_MJPEG_LEN)
-# endif
-#else
-# if 1 == CFG_TUD_VIDEO_STREAMING_BULK
-#  define CONFIG_TOTAL_LEN    (TUD_CONFIG_DESC_LEN + TUD_VIDEO_CAPTURE_DESC_UNCOMPR_BULK_LEN)
-# else
-#  define CONFIG_TOTAL_LEN    (TUD_CONFIG_DESC_LEN + TUD_VIDEO_CAPTURE_DESC_UNCOMPR_LEN)
-# endif
-#endif
-
+// Select appropriate endpoint number
 #if TU_CHECK_MCU(OPT_MCU_LPC175X_6X, OPT_MCU_LPC177X_8X, OPT_MCU_LPC40XX)
   // LPC 17xx and 40xx endpoint type (bulk/interrupt/iso) are fixed by its number
   // 0 control, 1 In, 2 Bulk, 3 Iso, 4 In, 5 Bulk etc ...
-#if 1 == CFG_TUD_VIDEO_STREAMING_BULK
-  #define EPNUM_VIDEO_IN    0x82
-#else
-  #define EPNUM_VIDEO_IN    0x83
-#endif
-
+  #define EPNUM_VIDEO_IN    (CFG_TUD_VIDEO_STREAMING_BULK ? 0x82 : 0x83)
 #elif TU_CHECK_MCU(OPT_MCU_NRF5X)
   // nRF5x ISO can only be endpoint 8
-  #define EPNUM_VIDEO_IN    0x88
-
+  #define EPNUM_VIDEO_IN    (CFG_TUD_VIDEO_STREAMING_BULK ? 0x81 : 0x88)
 #else
   #define EPNUM_VIDEO_IN    0x81
-
 #endif
 
-uint8_t const desc_fs_configuration[] =
-{
-  // Config number, interface count, string index, total length, attribute, power in mA
-  TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0, 500),
-
-  // IAD for Video Control
+// For mcus that does not have enough SRAM for frame buffer, we use fixed frame data.
+// To further reduce the size, we use MJPEG format instead of YUY2.
+// Select interface descriptor and length accordingly.
 #if defined(CFG_EXAMPLE_VIDEO_READONLY) && !defined(CFG_EXAMPLE_VIDEO_DISABLE_MJPEG)
-# if 1 == CFG_TUD_VIDEO_STREAMING_BULK
-  TUD_VIDEO_CAPTURE_DESCRIPTOR_MJPEG_BULK(4, EPNUM_VIDEO_IN,
-                                          FRAME_WIDTH, FRAME_HEIGHT, FRAME_RATE,
-                                          64)
-# else
-  TUD_VIDEO_CAPTURE_DESCRIPTOR_MJPEG(4, EPNUM_VIDEO_IN,
-                                     FRAME_WIDTH, FRAME_HEIGHT, FRAME_RATE,
-                                     CFG_TUD_VIDEO_STREAMING_EP_BUFSIZE)
-# endif
+  #if CFG_TUD_VIDEO_STREAMING_BULK
+    #define ITF_VIDEO_DESC(epsize)  TUD_VIDEO_CAPTURE_DESCRIPTOR_MJPEG_BULK(4, EPNUM_VIDEO_IN, FRAME_WIDTH, FRAME_HEIGHT, FRAME_RATE, epsize)
+    #define ITF_VIDEO_LEN           TUD_VIDEO_CAPTURE_DESC_MJPEG_BULK_LEN
+  #else
+    #define ITF_VIDEO_DESC(epsize)  TUD_VIDEO_CAPTURE_DESCRIPTOR_MJPEG(4, EPNUM_VIDEO_IN, FRAME_WIDTH, FRAME_HEIGHT, FRAME_RATE, epsize)
+    #define ITF_VIDEO_LEN           TUD_VIDEO_CAPTURE_DESC_MJPEG_LEN
+  #endif
 #else
-# if 1 == CFG_TUD_VIDEO_STREAMING_BULK
-  TUD_VIDEO_CAPTURE_DESCRIPTOR_UNCOMPR_BULK(4, EPNUM_VIDEO_IN,
-                                            FRAME_WIDTH, FRAME_HEIGHT, FRAME_RATE,
-                                            64)
-# else
-  TUD_VIDEO_CAPTURE_DESCRIPTOR_UNCOMPR(4, EPNUM_VIDEO_IN,
-                                       FRAME_WIDTH, FRAME_HEIGHT, FRAME_RATE,
-                                       CFG_TUD_VIDEO_STREAMING_EP_BUFSIZE)
-# endif
+  #if CFG_TUD_VIDEO_STREAMING_BULK
+    #define ITF_VIDEO_DESC(epsize)  TUD_VIDEO_CAPTURE_DESCRIPTOR_UNCOMPR_BULK(4, EPNUM_VIDEO_IN, FRAME_WIDTH, FRAME_HEIGHT, FRAME_RATE, epsize)
+    #define ITF_VIDEO_LEN           TUD_VIDEO_CAPTURE_DESC_UNCOMPR_BULK_LEN
+  #else
+    #define ITF_VIDEO_DESC(epsize)  TUD_VIDEO_CAPTURE_DESCRIPTOR_UNCOMPR(4, EPNUM_VIDEO_IN, FRAME_WIDTH, FRAME_HEIGHT, FRAME_RATE, epsize)
+    #define ITF_VIDEO_LEN           TUD_VIDEO_CAPTURE_DESC_UNCOMPR_LEN
+  #endif
 #endif
+
+#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + ITF_VIDEO_LEN)
+
+// full speed descriptor
+uint8_t const desc_fs_configuration[] = {
+    // Config number, interface count, string index, total length, attribute, power in mA
+    TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0, 500),
+
+    // IAD for Video Control
+    ITF_VIDEO_DESC(CFG_TUD_VIDEO_STREAMING_BULK ? 64 : CFG_TUD_VIDEO_STREAMING_EP_BUFSIZE)
 };
 
 #if TUD_OPT_HIGH_SPEED
 // Per USB specs: high speed capable device must report device_qualifier and other_speed_configuration
-
-uint8_t const desc_hs_configuration[] =
-{
+uint8_t const desc_hs_configuration[] = {
   // Config number, interface count, string index, total length, attribute, power in mA
   TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0, 500),
 
   // IAD for Video Control
-#if defined(CFG_EXAMPLE_VIDEO_READONLY) && !defined(CFG_EXAMPLE_VIDEO_DISABLE_MJPEG)
-# if 1 == CFG_TUD_VIDEO_STREAMING_BULK
-  TUD_VIDEO_CAPTURE_DESCRIPTOR_MJPEG_BULK(4, EPNUM_VIDEO_IN,
-                                          FRAME_WIDTH, FRAME_HEIGHT, FRAME_RATE,
-                                          512)
-# else
-  TUD_VIDEO_CAPTURE_DESCRIPTOR_MJPEG(4, EPNUM_VIDEO_IN,
-                                     FRAME_WIDTH, FRAME_HEIGHT, FRAME_RATE,
-                                     CFG_TUD_VIDEO_STREAMING_EP_BUFSIZE)
-# endif
-#else
-# if 1 == CFG_TUD_VIDEO_STREAMING_BULK
-  TUD_VIDEO_CAPTURE_DESCRIPTOR_UNCOMPR_BULK(4, EPNUM_VIDEO_IN,
-                                            FRAME_WIDTH, FRAME_HEIGHT, FRAME_RATE,
-                                            512)
-# else
-  TUD_VIDEO_CAPTURE_DESCRIPTOR_UNCOMPR(4, EPNUM_VIDEO_IN,
-                                       FRAME_WIDTH, FRAME_HEIGHT, FRAME_RATE,
-                                       CFG_TUD_VIDEO_STREAMING_EP_BUFSIZE)
-# endif
-#endif
+  ITF_VIDEO_DESC(CFG_TUD_VIDEO_STREAMING_BULK ? 512 : CFG_TUD_VIDEO_STREAMING_EP_BUFSIZE)
 };
 
 // device qualifier is mostly similar to device descriptor since we don't change configuration based on speed
-tusb_desc_device_qualifier_t const desc_device_qualifier =
-{
+tusb_desc_device_qualifier_t const desc_device_qualifier = {
   .bLength            = sizeof(tusb_desc_device_t),
   .bDescriptorType    = TUSB_DESC_DEVICE,
   .bcdUSB             = USB_BCD,
@@ -192,29 +150,24 @@ tusb_desc_device_qualifier_t const desc_device_qualifier =
 // Application return pointer to descriptor, whose contents must exist long enough for transfer to complete.
 // device_qualifier descriptor describes information about a high-speed capable device that would
 // change if the device were operating at the other speed. If not highspeed capable stall this request.
-uint8_t const* tud_descriptor_device_qualifier_cb(void)
-{
+uint8_t const* tud_descriptor_device_qualifier_cb(void) {
   return (uint8_t const*) &desc_device_qualifier;
 }
 
 // Invoked when received GET OTHER SEED CONFIGURATION DESCRIPTOR request
 // Application return pointer to descriptor, whose contents must exist long enough for transfer to complete
 // Configuration descriptor in the other speed e.g if high speed then this is for full speed and vice versa
-uint8_t const* tud_descriptor_other_speed_configuration_cb(uint8_t index)
-{
+uint8_t const* tud_descriptor_other_speed_configuration_cb(uint8_t index) {
   (void) index; // for multiple configurations
-
   // if link speed is high return fullspeed config, and vice versa
   return (tud_speed_get() == TUSB_SPEED_HIGH) ?  desc_fs_configuration : desc_hs_configuration;
 }
-
 #endif // highspeed
 
 // Invoked when received GET CONFIGURATION DESCRIPTOR
 // Application return pointer to descriptor
 // Descriptor contents must exist long enough for transfer to complete
-uint8_t const * tud_descriptor_configuration_cb(uint8_t index)
-{
+uint8_t const* tud_descriptor_configuration_cb(uint8_t index) {
   (void) index; // for multiple configurations
 
 #if TUD_OPT_HIGH_SPEED
@@ -238,24 +191,23 @@ enum {
 };
 
 // array of pointer to string descriptors
-char const *string_desc_arr[] =
-{
-  (const char[]) { 0x09, 0x04 }, // 0: is supported language is English (0x0409)
-  "TinyUSB",                     // 1: Manufacturer
-  "TinyUSB Device",              // 2: Product
-  NULL,                          // 3: Serials will use unique ID if possible
-  "TinyUSB UVC",                 // 4: UVC Interface
+char const* string_desc_arr[] = {
+    (const char[]) {0x09, 0x04}, // 0: is supported language is English (0x0409)
+    "TinyUSB",                     // 1: Manufacturer
+    "TinyUSB Device",              // 2: Product
+    NULL,                          // 3: Serials will use unique ID if possible
+    "TinyUSB UVC",                 // 4: UVC Interface
 };
 
 static uint16_t _desc_str[32 + 1];
 
 // Invoked when received GET STRING DESCRIPTOR request
 // Application return pointer to descriptor, whose contents must exist long enough for transfer to complete
-uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid) {
+uint16_t const* tud_descriptor_string_cb(uint8_t index, uint16_t langid) {
   (void) langid;
   size_t chr_count;
 
-  switch ( index ) {
+  switch (index) {
     case STRID_LANGID:
       memcpy(&_desc_str[1], string_desc_arr[0], 2);
       chr_count = 1;
@@ -269,17 +221,17 @@ uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid) {
       // Note: the 0xEE index string is a Microsoft OS 1.0 Descriptors.
       // https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-defined-usb-descriptors
 
-      if ( !(index < sizeof(string_desc_arr) / sizeof(string_desc_arr[0])) ) return NULL;
+      if (index >= sizeof(string_desc_arr) / sizeof(string_desc_arr[0])) return NULL;
 
-      const char *str = string_desc_arr[index];
+      const char* str = string_desc_arr[index];
 
       // Cap at max char
       chr_count = strlen(str);
       size_t const max_count = sizeof(_desc_str) / sizeof(_desc_str[0]) - 1; // -1 for string type
-      if ( chr_count > max_count ) chr_count = max_count;
+      if (chr_count > max_count) chr_count = max_count;
 
       // Convert ASCII string into UTF-16
-      for ( size_t i = 0; i < chr_count; i++ ) {
+      for (size_t i = 0; i < chr_count; i++) {
         _desc_str[1 + i] = str[i];
       }
       break;
diff --git a/examples/device/video_capture/src/usb_descriptors.h b/examples/device/video_capture/src/usb_descriptors.h
index b924c8dbe..fa3c1fbd2 100644
--- a/examples/device/video_capture/src/usb_descriptors.h
+++ b/examples/device/video_capture/src/usb_descriptors.h
@@ -126,23 +126,17 @@ enum {
 #define TUD_VIDEO_CAPTURE_DESCRIPTOR_UNCOMPR(_stridx, _epin, _width, _height, _fps, _epsize) \
   TUD_VIDEO_DESC_IAD(ITF_NUM_VIDEO_CONTROL, /* 2 Interfaces */ 0x02, _stridx), \
   /* Video control 0 */ \
-  TUD_VIDEO_DESC_STD_VC(ITF_NUM_VIDEO_CONTROL, 0, _stridx), \
-    TUD_VIDEO_DESC_CS_VC( /* UVC 1.5*/ 0x0150, \
-         /* wTotalLength - bLength */ \
-         TUD_VIDEO_DESC_CAMERA_TERM_LEN + TUD_VIDEO_DESC_OUTPUT_TERM_LEN, \
-         UVC_CLOCK_FREQUENCY, ITF_NUM_VIDEO_STREAMING), \
-      TUD_VIDEO_DESC_CAMERA_TERM(UVC_ENTITY_CAP_INPUT_TERMINAL, 0, 0,\
-                                 /*wObjectiveFocalLengthMin*/0, /*wObjectiveFocalLengthMax*/0,\
-                                 /*wObjectiveFocalLength*/0, /*bmControls*/0), \
+  TUD_VIDEO_DESC_STD_VC(ITF_NUM_VIDEO_CONTROL, 0, _stridx),                                  \
+    /* Header: UVC 1.5, length of followed descs, clock (deprecated), streaming interfaces */ \
+    TUD_VIDEO_DESC_CS_VC(0x0150, TUD_VIDEO_DESC_CAMERA_TERM_LEN + TUD_VIDEO_DESC_OUTPUT_TERM_LEN, UVC_CLOCK_FREQUENCY, ITF_NUM_VIDEO_STREAMING), \
+      /* Camera Terminal: ID, bAssocTerminal, iTerminal, focal min, max, length, bmControl */ \
+      TUD_VIDEO_DESC_CAMERA_TERM(UVC_ENTITY_CAP_INPUT_TERMINAL, 0, 0, 0, 0, 0, 0), \
       TUD_VIDEO_DESC_OUTPUT_TERM(UVC_ENTITY_CAP_OUTPUT_TERMINAL, VIDEO_TT_STREAMING, 0, 1, 0), \
   /* Video stream alt. 0 */ \
   TUD_VIDEO_DESC_STD_VS(ITF_NUM_VIDEO_STREAMING, 0, 0, _stridx), \
     /* Video stream header for without still image capture */ \
     TUD_VIDEO_DESC_CS_VS_INPUT( /*bNumFormats*/1, \
-        /*wTotalLength - bLength */\
-        TUD_VIDEO_DESC_CS_VS_FMT_UNCOMPR_LEN\
-        + TUD_VIDEO_DESC_CS_VS_FRM_UNCOMPR_CONT_LEN\
-        + TUD_VIDEO_DESC_CS_VS_COLOR_MATCHING_LEN,\
+        /*wTotalLength - bLength */ TUD_VIDEO_DESC_CS_VS_FMT_UNCOMPR_LEN + TUD_VIDEO_DESC_CS_VS_FRM_UNCOMPR_CONT_LEN + TUD_VIDEO_DESC_CS_VS_COLOR_MATCHING_LEN,\
         _epin, /*bmInfo*/0, /*bTerminalLink*/UVC_ENTITY_CAP_OUTPUT_TERMINAL, \
         /*bStillCaptureMethod*/0, /*bTriggerSupport*/0, /*bTriggerUsage*/0, \
         /*bmaControls(1)*/0), \
@@ -163,23 +157,17 @@ enum {
 #define TUD_VIDEO_CAPTURE_DESCRIPTOR_MJPEG(_stridx, _epin, _width, _height, _fps, _epsize) \
   TUD_VIDEO_DESC_IAD(ITF_NUM_VIDEO_CONTROL, /* 2 Interfaces */ 0x02, _stridx), \
   /* Video control 0 */ \
-  TUD_VIDEO_DESC_STD_VC(ITF_NUM_VIDEO_CONTROL, 0, _stridx), \
-    TUD_VIDEO_DESC_CS_VC( /* UVC 1.5*/ 0x0150, \
-         /* wTotalLength - bLength */ \
-         TUD_VIDEO_DESC_CAMERA_TERM_LEN + TUD_VIDEO_DESC_OUTPUT_TERM_LEN, \
-         UVC_CLOCK_FREQUENCY, ITF_NUM_VIDEO_STREAMING), \
-      TUD_VIDEO_DESC_CAMERA_TERM(UVC_ENTITY_CAP_INPUT_TERMINAL, 0, 0,\
-                                 /*wObjectiveFocalLengthMin*/0, /*wObjectiveFocalLengthMax*/0,\
-                                 /*wObjectiveFocalLength*/0, /*bmControls*/0), \
+  TUD_VIDEO_DESC_STD_VC(ITF_NUM_VIDEO_CONTROL, 0, _stridx),                                \
+    /* Header: UVC 1.5, length of followed descs, clock (deprecated), streaming interfaces */ \
+    TUD_VIDEO_DESC_CS_VC(0x0150, TUD_VIDEO_DESC_CAMERA_TERM_LEN + TUD_VIDEO_DESC_OUTPUT_TERM_LEN, UVC_CLOCK_FREQUENCY, ITF_NUM_VIDEO_STREAMING), \
+      /* Camera Terminal: ID, bAssocTerminal, iTerminal, focal min, max, length, bmControl */ \
+      TUD_VIDEO_DESC_CAMERA_TERM(UVC_ENTITY_CAP_INPUT_TERMINAL, 0, 0, 0, 0, 0, 0), \
       TUD_VIDEO_DESC_OUTPUT_TERM(UVC_ENTITY_CAP_OUTPUT_TERMINAL, VIDEO_TT_STREAMING, 0, 1, 0), \
   /* Video stream alt. 0 */ \
   TUD_VIDEO_DESC_STD_VS(ITF_NUM_VIDEO_STREAMING, 0, 0, _stridx), \
     /* Video stream header for without still image capture */ \
     TUD_VIDEO_DESC_CS_VS_INPUT( /*bNumFormats*/1, \
-        /*wTotalLength - bLength */\
-        TUD_VIDEO_DESC_CS_VS_FMT_MJPEG_LEN\
-        + TUD_VIDEO_DESC_CS_VS_FRM_MJPEG_CONT_LEN\
-        + TUD_VIDEO_DESC_CS_VS_COLOR_MATCHING_LEN,\
+        /*wTotalLength - bLength */ TUD_VIDEO_DESC_CS_VS_FMT_MJPEG_LEN + TUD_VIDEO_DESC_CS_VS_FRM_MJPEG_CONT_LEN + TUD_VIDEO_DESC_CS_VS_COLOR_MATCHING_LEN,\
         _epin, /*bmInfo*/0, /*bTerminalLink*/UVC_ENTITY_CAP_OUTPUT_TERMINAL, \
         /*bStillCaptureMethod*/0, /*bTriggerSupport*/0, /*bTriggerUsage*/0, \
         /*bmaControls(1)*/0), \
@@ -202,22 +190,17 @@ enum {
   TUD_VIDEO_DESC_IAD(ITF_NUM_VIDEO_CONTROL, /* 2 Interfaces */ 0x02, _stridx), \
   /* Video control 0 */ \
   TUD_VIDEO_DESC_STD_VC(ITF_NUM_VIDEO_CONTROL, 0, _stridx), \
-    TUD_VIDEO_DESC_CS_VC( /* UVC 1.5*/ 0x0150, \
-         /* wTotalLength - bLength */ \
-         TUD_VIDEO_DESC_CAMERA_TERM_LEN + TUD_VIDEO_DESC_OUTPUT_TERM_LEN, \
-         UVC_CLOCK_FREQUENCY, ITF_NUM_VIDEO_STREAMING), \
-      TUD_VIDEO_DESC_CAMERA_TERM(UVC_ENTITY_CAP_INPUT_TERMINAL, 0, 0,\
-                                 /*wObjectiveFocalLengthMin*/0, /*wObjectiveFocalLengthMax*/0,\
-                                 /*wObjectiveFocalLength*/0, /*bmControls*/0), \
+    /* Header: UVC 1.5, length of followed descs, clock (deprecated), streaming interfaces */ \
+    TUD_VIDEO_DESC_CS_VC(0x0150, TUD_VIDEO_DESC_CAMERA_TERM_LEN + TUD_VIDEO_DESC_OUTPUT_TERM_LEN, UVC_CLOCK_FREQUENCY, ITF_NUM_VIDEO_STREAMING), \
+      /* Camera Terminal: ID, bAssocTerminal, iTerminal, focal min, max, length, bmControl */ \
+      TUD_VIDEO_DESC_CAMERA_TERM(UVC_ENTITY_CAP_INPUT_TERMINAL, 0, 0, 0, 0, 0, 0), \
       TUD_VIDEO_DESC_OUTPUT_TERM(UVC_ENTITY_CAP_OUTPUT_TERMINAL, VIDEO_TT_STREAMING, 0, 1, 0), \
   /* Video stream alt. 0 */ \
   TUD_VIDEO_DESC_STD_VS(ITF_NUM_VIDEO_STREAMING, 0, 1, _stridx), \
     /* Video stream header for without still image capture */ \
     TUD_VIDEO_DESC_CS_VS_INPUT( /*bNumFormats*/1, \
         /*wTotalLength - bLength */\
-        TUD_VIDEO_DESC_CS_VS_FMT_UNCOMPR_LEN\
-        + TUD_VIDEO_DESC_CS_VS_FRM_UNCOMPR_CONT_LEN\
-        + TUD_VIDEO_DESC_CS_VS_COLOR_MATCHING_LEN,\
+        TUD_VIDEO_DESC_CS_VS_FMT_UNCOMPR_LEN + TUD_VIDEO_DESC_CS_VS_FRM_UNCOMPR_CONT_LEN + TUD_VIDEO_DESC_CS_VS_COLOR_MATCHING_LEN,\
         _epin, /*bmInfo*/0, /*bTerminalLink*/UVC_ENTITY_CAP_OUTPUT_TERMINAL, \
         /*bStillCaptureMethod*/0, /*bTriggerSupport*/0, /*bTriggerUsage*/0, \
         /*bmaControls(1)*/0), \
@@ -235,23 +218,17 @@ enum {
 #define TUD_VIDEO_CAPTURE_DESCRIPTOR_MJPEG_BULK(_stridx, _epin, _width, _height, _fps, _epsize) \
   TUD_VIDEO_DESC_IAD(ITF_NUM_VIDEO_CONTROL, /* 2 Interfaces */ 0x02, _stridx), \
   /* Video control 0 */ \
-  TUD_VIDEO_DESC_STD_VC(ITF_NUM_VIDEO_CONTROL, 0, _stridx), \
-    TUD_VIDEO_DESC_CS_VC( /* UVC 1.5*/ 0x0150, \
-         /* wTotalLength - bLength */ \
-         TUD_VIDEO_DESC_CAMERA_TERM_LEN + TUD_VIDEO_DESC_OUTPUT_TERM_LEN, \
-         UVC_CLOCK_FREQUENCY, ITF_NUM_VIDEO_STREAMING), \
-      TUD_VIDEO_DESC_CAMERA_TERM(UVC_ENTITY_CAP_INPUT_TERMINAL, 0, 0,\
-                                 /*wObjectiveFocalLengthMin*/0, /*wObjectiveFocalLengthMax*/0,\
-                                 /*wObjectiveFocalLength*/0, /*bmControls*/0), \
+  TUD_VIDEO_DESC_STD_VC(ITF_NUM_VIDEO_CONTROL, 0, _stridx),                                     \
+    /* Header: UVC 1.5, length of followed descs, clock (deprecated), streaming interfaces */ \
+    TUD_VIDEO_DESC_CS_VC(0x0150, TUD_VIDEO_DESC_CAMERA_TERM_LEN + TUD_VIDEO_DESC_OUTPUT_TERM_LEN, UVC_CLOCK_FREQUENCY, ITF_NUM_VIDEO_STREAMING), \
+      /* Camera Terminal: ID, bAssocTerminal, iTerminal, focal min, max, length, bmControl */ \
+      TUD_VIDEO_DESC_CAMERA_TERM(UVC_ENTITY_CAP_INPUT_TERMINAL, 0, 0, 0, 0, 0, 0), \
       TUD_VIDEO_DESC_OUTPUT_TERM(UVC_ENTITY_CAP_OUTPUT_TERMINAL, VIDEO_TT_STREAMING, 0, 1, 0), \
   /* Video stream alt. 0 */ \
   TUD_VIDEO_DESC_STD_VS(ITF_NUM_VIDEO_STREAMING, 0, 1, _stridx), \
     /* Video stream header for without still image capture */ \
     TUD_VIDEO_DESC_CS_VS_INPUT( /*bNumFormats*/1, \
-        /*wTotalLength - bLength */\
-        TUD_VIDEO_DESC_CS_VS_FMT_MJPEG_LEN\
-        + TUD_VIDEO_DESC_CS_VS_FRM_MJPEG_CONT_LEN\
-        + TUD_VIDEO_DESC_CS_VS_COLOR_MATCHING_LEN,\
+        /*wTotalLength - bLength */ TUD_VIDEO_DESC_CS_VS_FMT_MJPEG_LEN + TUD_VIDEO_DESC_CS_VS_FRM_MJPEG_CONT_LEN + TUD_VIDEO_DESC_CS_VS_COLOR_MATCHING_LEN,\
         _epin, /*bmInfo*/0, /*bTerminalLink*/UVC_ENTITY_CAP_OUTPUT_TERMINAL, \
         /*bStillCaptureMethod*/0, /*bTriggerSupport*/0, /*bTriggerUsage*/0, \
         /*bmaControls(1)*/0), \
diff --git a/examples/host/cdc_msc_hid/src/cdc_app.c b/examples/host/cdc_msc_hid/src/cdc_app.c
index a1b26e49c..e275e7943 100644
--- a/examples/host/cdc_msc_hid/src/cdc_app.c
+++ b/examples/host/cdc_msc_hid/src/cdc_app.c
@@ -27,20 +27,11 @@
 #include "tusb.h"
 #include "bsp/board_api.h"
 
-//--------------------------------------------------------------------+
-// MACRO TYPEDEF CONSTANT ENUM DECLARATION
-//--------------------------------------------------------------------+
-
-
-//------------- IMPLEMENTATION -------------//
-
-size_t get_console_inputs(uint8_t* buf, size_t bufsize)
-{
+size_t get_console_inputs(uint8_t* buf, size_t bufsize) {
   size_t count = 0;
-  while (count < bufsize)
-  {
+  while (count < bufsize) {
     int ch = board_getchar();
-    if ( ch <= 0 ) break;
+    if (ch <= 0) break;
 
     buf[count] = (uint8_t) ch;
     count++;
@@ -49,22 +40,18 @@ size_t get_console_inputs(uint8_t* buf, size_t bufsize)
   return count;
 }
 
-void cdc_app_task(void)
-{
-  uint8_t buf[64+1]; // +1 for extra null character
-  uint32_t const bufsize = sizeof(buf)-1;
+void cdc_app_task(void) {
+  uint8_t buf[64 + 1]; // +1 for extra null character
+  uint32_t const bufsize = sizeof(buf) - 1;
 
   uint32_t count = get_console_inputs(buf, bufsize);
   buf[count] = 0;
 
   // loop over all mounted interfaces
-  for(uint8_t idx=0; idx cdc interfaces
-      if (count)
-      {
+      if (count) {
         tuh_cdc_write(idx, buf, count);
         tuh_cdc_write_flush(idx);
       }
@@ -72,11 +59,14 @@ void cdc_app_task(void)
   }
 }
 
+//--------------------------------------------------------------------+
+// 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;
+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);
@@ -85,29 +75,35 @@ void tuh_cdc_rx_cb(uint8_t idx)
   printf((char*) buf);
 }
 
-void tuh_cdc_mount_cb(uint8_t idx)
-{
-  tuh_itf_info_t itf_info = { 0 };
+// Invoked when a device with CDC interface is mounted
+// idx is index of cdc interface in the internal pool.
+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);
+  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) )
-  {
+  // If CFG_TUH_CDC_LINE_CODING_ON_ENUM is defined, line coding will be set by tinyusb stack
+  // while eneumerating new cdc device
+  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);
+    printf("  Parity  : %u, Data Width: %u\r\n", line_coding.parity, line_coding.data_bits);
   }
+#else
+  // Set Line Coding upon mounted
+  cdc_line_coding_t new_line_coding = { 115200, CDC_LINE_CODING_STOP_BITS_1, CDC_LINE_CODING_PARITY_NONE, 8 };
+  tuh_cdc_set_line_coding(idx, &new_line_coding, NULL, 0);
 #endif
 }
 
-void tuh_cdc_umount_cb(uint8_t idx)
-{
-  tuh_itf_info_t itf_info = { 0 };
+// Invoked when a device with CDC interface is unmounted
+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);
+  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/src/tusb_config.h b/examples/host/cdc_msc_hid/src/tusb_config.h
index 61abb85eb..76d59c316 100644
--- a/examples/host/cdc_msc_hid/src/tusb_config.h
+++ b/examples/host/cdc_msc_hid/src/tusb_config.h
@@ -105,6 +105,7 @@
 #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_CDC_CH34X           1 // CH340 or CH341 Serial. CH34X 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
diff --git a/examples/host/cdc_msc_hid_freertos/src/tusb_config.h b/examples/host/cdc_msc_hid_freertos/src/tusb_config.h
index c661f47be..bb7c3388d 100644
--- a/examples/host/cdc_msc_hid_freertos/src/tusb_config.h
+++ b/examples/host/cdc_msc_hid_freertos/src/tusb_config.h
@@ -110,6 +110,7 @@
 #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_CDC_CH34X           1 // CH340 or CH341 Serial. CH34X 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
diff --git a/hw/bsp/family_support.cmake b/hw/bsp/family_support.cmake
index 539a776a6..ff3393cfd 100644
--- a/hw/bsp/family_support.cmake
+++ b/hw/bsp/family_support.cmake
@@ -211,7 +211,6 @@ function(family_configure_common TARGET RTOS)
     target_link_options(${TARGET} PUBLIC "LINKER:--map=$.map")
   endif()
 
-
   # ETM Trace option
   if (TRACE_ETM STREQUAL "1")
     target_compile_definitions(${TARGET} PUBLIC TRACE_ETM)
diff --git a/hw/bsp/lpc17/FreeRTOSConfig/FreeRTOSConfig.h b/hw/bsp/lpc17/FreeRTOSConfig/FreeRTOSConfig.h
new file mode 100644
index 000000000..9b66fb110
--- /dev/null
+++ b/hw/bsp/lpc17/FreeRTOSConfig/FreeRTOSConfig.h
@@ -0,0 +1,149 @@
+/*
+ * FreeRTOS Kernel V10.0.0
+ * Copyright (C) 2017 Amazon.com, Inc. or its affiliates.  All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+ * the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. If you wish to use our Amazon
+ * FreeRTOS name, please do so in a fair use way that does not cause confusion.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * http://www.FreeRTOS.org
+ * http://aws.amazon.com/freertos
+ *
+ * 1 tab == 4 spaces!
+ */
+
+
+#ifndef FREERTOS_CONFIG_H
+#define FREERTOS_CONFIG_H
+
+/*-----------------------------------------------------------
+ * Application specific definitions.
+ *
+ * These definitions should be adjusted for your particular hardware and
+ * application requirements.
+ *
+ * THESE PARAMETERS ARE DESCRIBED WITHIN THE 'CONFIGURATION' SECTION OF THE
+ * FreeRTOS API DOCUMENTATION AVAILABLE ON THE FreeRTOS.org WEB SITE.
+ *
+ * See http://www.freertos.org/a00110.html.
+ *----------------------------------------------------------*/
+
+// skip if included from IAR assembler
+#ifndef __IASMARM__
+  #include "chip.h"
+#endif
+
+/* Cortex M23/M33 port configuration. */
+#define configENABLE_MPU                        0
+#define configENABLE_FPU                        0
+#define configENABLE_TRUSTZONE                  0
+#define configMINIMAL_SECURE_STACK_SIZE         (1024)
+
+#define configUSE_PREEMPTION                    1
+#define configUSE_PORT_OPTIMISED_TASK_SELECTION 0
+#define configCPU_CLOCK_HZ                      SystemCoreClock
+#define configTICK_RATE_HZ                      ( 1000 )
+#define configMAX_PRIORITIES                    ( 5 )
+#define configMINIMAL_STACK_SIZE                ( 128 )
+#define configTOTAL_HEAP_SIZE                   ( configSUPPORT_DYNAMIC_ALLOCATION*4*1024 )
+#define configMAX_TASK_NAME_LEN                 16
+#define configUSE_16_BIT_TICKS                  0
+#define configIDLE_SHOULD_YIELD                 1
+#define configUSE_MUTEXES                       1
+#define configUSE_RECURSIVE_MUTEXES             1
+#define configUSE_COUNTING_SEMAPHORES           1
+#define configQUEUE_REGISTRY_SIZE               4
+#define configUSE_QUEUE_SETS                    0
+#define configUSE_TIME_SLICING                  0
+#define configUSE_NEWLIB_REENTRANT              0
+#define configENABLE_BACKWARD_COMPATIBILITY     1
+#define configSTACK_ALLOCATION_FROM_SEPARATE_HEAP   0
+
+#define configSUPPORT_STATIC_ALLOCATION         1
+#define configSUPPORT_DYNAMIC_ALLOCATION        0
+
+/* Hook function related definitions. */
+#define configUSE_IDLE_HOOK                    0
+#define configUSE_TICK_HOOK                    0
+#define configUSE_MALLOC_FAILED_HOOK           0 // cause nested extern warning
+#define configCHECK_FOR_STACK_OVERFLOW         2
+#define configCHECK_HANDLER_INSTALLATION       0
+
+/* Run time and task stats gathering related definitions. */
+#define configGENERATE_RUN_TIME_STATS          0
+#define configRECORD_STACK_HIGH_ADDRESS        1
+#define configUSE_TRACE_FACILITY               1 // legacy trace
+#define configUSE_STATS_FORMATTING_FUNCTIONS   0
+
+/* Co-routine definitions. */
+#define configUSE_CO_ROUTINES                  0
+#define configMAX_CO_ROUTINE_PRIORITIES        2
+
+/* Software timer related definitions. */
+#define configUSE_TIMERS                       1
+#define configTIMER_TASK_PRIORITY              (configMAX_PRIORITIES-2)
+#define configTIMER_QUEUE_LENGTH               32
+#define configTIMER_TASK_STACK_DEPTH           configMINIMAL_STACK_SIZE
+
+/* Optional functions - most linkers will remove unused functions anyway. */
+#define INCLUDE_vTaskPrioritySet               0
+#define INCLUDE_uxTaskPriorityGet              0
+#define INCLUDE_vTaskDelete                    0
+#define INCLUDE_vTaskSuspend                   1 // required for queue, semaphore, mutex to be blocked indefinitely with portMAX_DELAY
+#define INCLUDE_xResumeFromISR                 0
+#define INCLUDE_vTaskDelayUntil                1
+#define INCLUDE_vTaskDelay                     1
+#define INCLUDE_xTaskGetSchedulerState         0
+#define INCLUDE_xTaskGetCurrentTaskHandle      1
+#define INCLUDE_uxTaskGetStackHighWaterMark    0
+#define INCLUDE_xTaskGetIdleTaskHandle         0
+#define INCLUDE_xTimerGetTimerDaemonTaskHandle 0
+#define INCLUDE_pcTaskGetTaskName              0
+#define INCLUDE_eTaskGetState                  0
+#define INCLUDE_xEventGroupSetBitFromISR       0
+#define INCLUDE_xTimerPendFunctionCall         0
+
+/* FreeRTOS hooks to NVIC vectors */
+#define xPortPendSVHandler    PendSV_Handler
+#define xPortSysTickHandler   SysTick_Handler
+#define vPortSVCHandler       SVC_Handler
+
+//--------------------------------------------------------------------+
+// Interrupt nesting behavior configuration.
+//--------------------------------------------------------------------+
+
+// For Cortex-M specific: __NVIC_PRIO_BITS is defined in mcu header
+#define configPRIO_BITS       3
+
+/* The lowest interrupt priority that can be used in a call to a "set priority" function. */
+#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY			  ((1<OTGClkCtrl = clk_en;
-  while ( (LPC_USB->OTGClkSt & clk_en) != clk_en );
-
-#if CFG_TUH_ENABLED
-  // set portfunc to host !!!
-  LPC_USB->StCtrl = 0x3; // should be 1
-#endif
-}
-
-//--------------------------------------------------------------------+
-// Board porting API
-//--------------------------------------------------------------------+
-
-void board_led_write(bool state)
-{
-  Chip_GPIO_SetPinState(LPC_GPIO, LED_PORT, LED_PIN, state ? LED_STATE_ON : (1-LED_STATE_ON));
-}
-
-uint32_t board_button_read(void)
-{
-  return BUTTON_STATE_ACTIVE == Chip_GPIO_GetPinState(LPC_GPIO, BUTTON_PORT, BUTTON_PIN);
-}
-
-int board_uart_read(uint8_t* buf, int len)
-{
-//  return UART_ReceiveByte(BOARD_UART_PORT);
-  (void) buf; (void) len;
-  return 0;
-}
-
-int board_uart_write(void const * buf, int len)
-{
-//  UART_Send(BOARD_UART_PORT, &c, 1, BLOCKING);
-  (void) buf; (void) len;
-  return 0;
-}
-
-#if CFG_TUSB_OS == OPT_OS_NONE
-volatile uint32_t system_ticks = 0;
-void SysTick_Handler (void)
-{
-  system_ticks++;
-}
-
-uint32_t board_millis(void)
-{
-  return system_ticks;
-}
-#endif
diff --git a/hw/bsp/lpc17/boards/mbed1768/board.cmake b/hw/bsp/lpc17/boards/mbed1768/board.cmake
new file mode 100644
index 000000000..688f34292
--- /dev/null
+++ b/hw/bsp/lpc17/boards/mbed1768/board.cmake
@@ -0,0 +1,11 @@
+set(MCU_VARIANT LPC1768)
+
+set(JLINK_DEVICE LPC1768)
+set(PYOCD_TARGET LPC1768)
+set(NXPLINK_DEVICE LPC1768:LPC1768)
+
+set(LD_FILE_GNU ${CMAKE_CURRENT_LIST_DIR}/lpc1768.ld)
+
+function(update_board TARGET)
+  # nothing to do
+endfunction()
diff --git a/hw/bsp/lpc17/boards/mbed1768/board.h b/hw/bsp/lpc17/boards/mbed1768/board.h
new file mode 100644
index 000000000..2b3ddc905
--- /dev/null
+++ b/hw/bsp/lpc17/boards/mbed1768/board.h
@@ -0,0 +1,71 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021, Ha Thach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#ifndef BOARD_H_
+#define BOARD_H_
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+#define LED_PORT              1
+#define LED_PIN               18
+#define LED_STATE_ON          1
+
+// JOYSTICK_DOWN if using LPCXpresso Base Board
+#define BUTTON_PORT           0
+#define BUTTON_PIN            15
+#define BUTTON_STATE_ACTIVE   0
+
+#define BOARD_UART_PORT   LPC_UART3
+
+/* System oscillator rate and RTC oscillator rate */
+const uint32_t OscRateIn = 10000000;
+const uint32_t RTCOscRateIn = 32768;
+
+// Pin muxing configuration
+static const PINMUX_GRP_T pinmuxing[] = {
+    {LED_PORT,  LED_PIN,  IOCON_MODE_INACT | IOCON_FUNC0},
+    {BUTTON_PORT, BUTTON_PIN, IOCON_FUNC0 | IOCON_MODE_PULLUP},
+};
+
+static const PINMUX_GRP_T pin_usb_mux[] = {
+    {0, 29, IOCON_MODE_INACT | IOCON_FUNC1}, // D+
+    {0, 30, IOCON_MODE_INACT | IOCON_FUNC1}, // D-
+    {2, 9,  IOCON_MODE_INACT | IOCON_FUNC1}, // Soft Connect
+
+    {1, 19, IOCON_MODE_INACT | IOCON_FUNC2}, // USB_PPWR (Host mode)
+    {1, 22, IOCON_MODE_INACT | IOCON_FUNC2}, // USB_PWRD
+
+    // VBUS is not connected on this board, so leave the pin at default setting.
+    // Chip_IOCON_PinMux(LPC_IOCON, 1, 30, IOCON_MODE_INACT, IOCON_FUNC2);  // USB VBUS
+};
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif
diff --git a/hw/bsp/lpc17/boards/mbed1768/mbed1768.c b/hw/bsp/lpc17/family.c
similarity index 61%
rename from hw/bsp/lpc17/boards/mbed1768/mbed1768.c
rename to hw/bsp/lpc17/family.c
index 613dcb570..79281ba41 100644
--- a/hw/bsp/lpc17/boards/mbed1768/mbed1768.c
+++ b/hw/bsp/lpc17/family.c
@@ -26,58 +26,24 @@
 
 #include "chip.h"
 #include "bsp/board_api.h"
-
-#define LED_PORT              1
-#define LED_PIN               18
-#define LED_STATE_ON          1
-
-// JOYSTICK_DOWN if using LPCXpresso Base Board
-#define BUTTON_PORT           0
-#define BUTTON_PIN            15
-#define BUTTON_STATE_ACTIVE   0
-
-#define BOARD_UART_PORT   LPC_UART3
-
-/* System oscillator rate and RTC oscillator rate */
-const uint32_t OscRateIn = 10000000;
-const uint32_t RTCOscRateIn = 32768;
-
-/* Pin muxing configuration */
-static const PINMUX_GRP_T pinmuxing[] =
-{
-  {LED_PORT,  LED_PIN,  IOCON_MODE_INACT | IOCON_FUNC0},
-  {BUTTON_PORT, BUTTON_PIN, IOCON_FUNC0 | IOCON_MODE_PULLUP},
-};
-
-static const PINMUX_GRP_T pin_usb_mux[] =
-{
-  {0, 29, IOCON_MODE_INACT | IOCON_FUNC1}, // D+
-  {0, 30, IOCON_MODE_INACT | IOCON_FUNC1}, // D-
-  {2,  9, IOCON_MODE_INACT | IOCON_FUNC1}, // Connect
-
-  {1, 19, IOCON_MODE_INACT | IOCON_FUNC2}, // USB_PPWR
-  {1, 22, IOCON_MODE_INACT | IOCON_FUNC2}, // USB_PWRD
-
-  /* VBUS is not connected on this board, so leave the pin at default setting. */
-  /*Chip_IOCON_PinMux(LPC_IOCON, 1, 30, IOCON_MODE_INACT, IOCON_FUNC2);*/ /* USB VBUS */
-};
+#include "board.h"
 
 // Invoked by startup code
-void SystemInit(void)
-{
+void SystemInit(void) {
 #ifdef __USE_LPCOPEN
-	extern void (* const g_pfnVectors[])(void);
-  unsigned int *pSCB_VTOR = (unsigned int *) 0xE000ED08;
-	*pSCB_VTOR = (unsigned int) g_pfnVectors;
+  extern void (* const g_pfnVectors[])(void);
+  unsigned int* pSCB_VTOR = (unsigned int*) 0xE000ED08;
+  *pSCB_VTOR = (unsigned int) g_pfnVectors;
 #endif
 
   Chip_IOCON_Init(LPC_IOCON);
   Chip_IOCON_SetPinMuxing(LPC_IOCON, pinmuxing, sizeof(pinmuxing) / sizeof(PINMUX_GRP_T));
   Chip_SetupXtalClocking();
+
+  Chip_SYSCTL_SetFLASHAccess(FLASHTIM_100MHZ_CPU);
 }
 
-void board_init(void)
-{
+void board_init(void) {
   SystemCoreClockUpdate();
 
 #if CFG_TUSB_OS == OPT_OS_NONE
@@ -89,11 +55,7 @@ void board_init(void)
 #endif
 
   Chip_GPIO_Init(LPC_GPIO);
-
-  // LED
   Chip_GPIO_SetPinDIROutput(LPC_GPIO, LED_PORT, LED_PIN);
-
-  // Button
   Chip_GPIO_SetPinDIRInput(LPC_GPIO, BUTTON_PORT, BUTTON_PIN);
 
 #if 0
@@ -106,34 +68,34 @@ void board_init(void)
       .OpenDrain = 0,
       .Pinmode   = 0
   };
-	PINSEL_ConfigPin(&PinCfg);
+  PINSEL_ConfigPin(&PinCfg);
 
-	PinCfg.Portnum = 0;
-	PinCfg.Pinnum  = 1; // RXD is P0.1
-	PINSEL_ConfigPin(&PinCfg);
+  PinCfg.Portnum = 0;
+  PinCfg.Pinnum  = 1; // RXD is P0.1
+  PINSEL_ConfigPin(&PinCfg);
 
-	UART_CFG_Type UARTConfigStruct;
+  UART_CFG_Type UARTConfigStruct;
   UART_ConfigStructInit(&UARTConfigStruct);
-	UARTConfigStruct.Baud_rate = CFG_BOARD_UART_BAUDRATE;
+  UARTConfigStruct.Baud_rate = CFG_BOARD_UART_BAUDRATE;
 
-	UART_Init(BOARD_UART_PORT, &UARTConfigStruct);
-	UART_TxCmd(BOARD_UART_PORT, ENABLE); // Enable UART Transmit
+  UART_Init(BOARD_UART_PORT, &UARTConfigStruct);
+  UART_TxCmd(BOARD_UART_PORT, ENABLE); // Enable UART Transmit
 #endif
 
-	//------------- USB -------------//
+  //------------- USB -------------//
   Chip_IOCON_SetPinMuxing(LPC_IOCON, pin_usb_mux, sizeof(pin_usb_mux) / sizeof(PINMUX_GRP_T));
-	Chip_USB_Init();
+  Chip_USB_Init();
 
   enum {
     USBCLK_DEVCIE = 0x12,     // AHB + Device
-    USBCLK_HOST   = 0x19,     // AHB + Host + OTG
+    USBCLK_HOST = 0x19,     // AHB + Host + OTG
 //    0x1B // Host + Device + OTG + AHB
   };
 
   uint32_t const clk_en = CFG_TUD_ENABLED ? USBCLK_DEVCIE : USBCLK_HOST;
 
   LPC_USB->OTGClkCtrl = clk_en;
-  while ( (LPC_USB->OTGClkSt & clk_en) != clk_en );
+  while ((LPC_USB->OTGClkSt & clk_en) != clk_en) {}
 
 #if CFG_TUH_ENABLED
   // set portfunc to host !!!
@@ -141,57 +103,53 @@ void board_init(void)
 #endif
 }
 
-//--------------------------------------------------------------------+
-// USB Interrupt Handler
-//--------------------------------------------------------------------+
-void USB_IRQHandler(void)
-{
-  #if CFG_TUD_ENABLED
-    tud_int_handler(0);
-  #endif
-
-  #if CFG_TUH_ENABLED
-    tuh_int_handler(0, true);
-  #endif
-}
-
 //--------------------------------------------------------------------+
 // Board porting API
 //--------------------------------------------------------------------+
-
-void board_led_write(bool state)
-{
-  Chip_GPIO_SetPinState(LPC_GPIO, LED_PORT, LED_PIN, state ? LED_STATE_ON : (1-LED_STATE_ON));
+void board_led_write(bool state) {
+  Chip_GPIO_SetPinState(LPC_GPIO, LED_PORT, LED_PIN, state ? LED_STATE_ON : (1 - LED_STATE_ON));
 }
 
-uint32_t board_button_read(void)
-{
+uint32_t board_button_read(void) {
   return BUTTON_STATE_ACTIVE == Chip_GPIO_GetPinState(LPC_GPIO, BUTTON_PORT, BUTTON_PIN);
 }
 
-int board_uart_read(uint8_t* buf, int len)
-{
+int board_uart_read(uint8_t* buf, int len) {
 //  return UART_ReceiveByte(BOARD_UART_PORT);
-  (void) buf; (void) len;
+  (void) buf;
+  (void) len;
   return 0;
 }
 
-int board_uart_write(void const * buf, int len)
-{
+int board_uart_write(void const* buf, int len) {
 //  UART_Send(BOARD_UART_PORT, &c, 1, BLOCKING);
-  (void) buf; (void) len;
+  (void) buf;
+  (void) len;
   return 0;
 }
 
 #if CFG_TUSB_OS == OPT_OS_NONE
 volatile uint32_t system_ticks = 0;
-void SysTick_Handler (void)
-{
+
+void SysTick_Handler(void) {
   system_ticks++;
 }
 
-uint32_t board_millis(void)
-{
+uint32_t board_millis(void) {
   return system_ticks;
 }
+
+//--------------------------------------------------------------------+
+// USB Interrupt Handler
+//--------------------------------------------------------------------+
+void USB_IRQHandler(void) {
+  #if CFG_TUD_ENABLED
+  tud_int_handler(0);
+  #endif
+
+  #if CFG_TUH_ENABLED
+  tuh_int_handler(0, true);
+  #endif
+}
+
 #endif
diff --git a/hw/bsp/lpc17/family.cmake b/hw/bsp/lpc17/family.cmake
new file mode 100644
index 000000000..63ac3149c
--- /dev/null
+++ b/hw/bsp/lpc17/family.cmake
@@ -0,0 +1,102 @@
+include_guard()
+
+if (NOT BOARD)
+  message(FATAL_ERROR "BOARD not specified")
+endif ()
+
+set(SDK_DIR ${TOP}/hw/mcu/nxp/lpcopen/lpc175x_6x/lpc_chip_175x_6x)
+
+# include board specific
+include(${CMAKE_CURRENT_LIST_DIR}/boards/${BOARD}/board.cmake)
+
+# toolchain set up
+set(CMAKE_SYSTEM_PROCESSOR cortex-m3 CACHE INTERNAL "System Processor")
+set(CMAKE_TOOLCHAIN_FILE ${TOP}/examples/build_system/cmake/toolchain/arm_${TOOLCHAIN}.cmake)
+
+set(FAMILY_MCUS LPC175X_6X CACHE INTERNAL "")
+
+
+#------------------------------------
+# BOARD_TARGET
+#------------------------------------
+# only need to be built ONCE for all examples
+function(add_board_target BOARD_TARGET)
+  if (NOT TARGET ${BOARD_TARGET})
+    add_library(${BOARD_TARGET} STATIC
+      ${SDK_DIR}/../gcc/cr_startup_lpc175x_6x.c
+      ${SDK_DIR}/src/chip_17xx_40xx.c
+      ${SDK_DIR}/src/clock_17xx_40xx.c
+      ${SDK_DIR}/src/gpio_17xx_40xx.c
+      ${SDK_DIR}/src/iocon_17xx_40xx.c
+      ${SDK_DIR}/src/sysctl_17xx_40xx.c
+      ${SDK_DIR}/src/sysinit_17xx_40xx.c
+      ${SDK_DIR}/src/uart_17xx_40xx.c
+      )
+    target_compile_options(${BOARD_TARGET} PUBLIC
+      -nostdlib
+      )
+    target_compile_definitions(${BOARD_TARGET} PUBLIC
+      __USE_LPCOPEN
+      CORE_M3
+      RTC_EV_SUPPORT=0
+      )
+    target_include_directories(${BOARD_TARGET} PUBLIC
+      ${SDK_DIR}/inc
+      )
+
+    update_board(${BOARD_TARGET})
+
+    if (CMAKE_C_COMPILER_ID STREQUAL "GNU")
+      target_link_options(${BOARD_TARGET} PUBLIC
+        "LINKER:--script=${LD_FILE_GNU}"
+        # nanolib
+        --specs=nosys.specs --specs=nano.specs
+        )
+    elseif (CMAKE_C_COMPILER_ID STREQUAL "IAR")
+      target_link_options(${BOARD_TARGET} PUBLIC
+        "LINKER:--config=${LD_FILE_IAR}"
+        )
+    endif ()
+  endif ()
+endfunction()
+
+
+#------------------------------------
+# Functions
+#------------------------------------
+function(family_configure_example TARGET RTOS)
+  family_configure_common(${TARGET} ${RTOS})
+
+  # Board target
+  add_board_target(board_${BOARD})
+
+  #---------- Port Specific ----------
+  # These files are built for each example since it depends on example's tusb_config.h
+  target_sources(${TARGET} PUBLIC
+    # BSP
+    ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/family.c
+    ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/../board.c
+    )
+  target_include_directories(${TARGET} PUBLIC
+    # family, hw, board
+    ${CMAKE_CURRENT_FUNCTION_LIST_DIR}
+    ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/../../
+    ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/boards/${BOARD}
+    )
+
+  # Add TinyUSB target and port source
+  family_add_tinyusb(${TARGET} OPT_MCU_LPC175X_6X ${RTOS})
+  target_sources(${TARGET}-tinyusb PUBLIC
+    ${TOP}/src/portable/nxp/lpc17_40/dcd_lpc17_40.c
+    ${TOP}/src/portable/nxp/lpc17_40/hcd_lpc17_40.c
+    ${TOP}/src/portable/ohci/ohci.c
+    )
+  target_link_libraries(${TARGET}-tinyusb PUBLIC board_${BOARD})
+
+  # Link dependencies
+  target_link_libraries(${TARGET} PUBLIC board_${BOARD} ${TARGET}-tinyusb)
+
+  # Flashing
+  family_flash_jlink(${TARGET})
+  #family_flash_nxplink(${TARGET})
+endfunction()
diff --git a/hw/bsp/lpc17/family.mk b/hw/bsp/lpc17/family.mk
index 67d5e14b5..694b6cccf 100644
--- a/hw/bsp/lpc17/family.mk
+++ b/hw/bsp/lpc17/family.mk
@@ -34,4 +34,5 @@ SRC_C += \
 	$(MCU_DIR)/src/uart_17xx_40xx.c \
 
 INC += \
-	$(TOP)/$(MCU_DIR)/inc
+	$(TOP)/$(BOARD_PATH) \
+	$(TOP)/$(MCU_DIR)/inc \
diff --git a/src/class/audio/audio_device.c b/src/class/audio/audio_device.c
index d438d0715..4adc558a6 100644
--- a/src/class/audio/audio_device.c
+++ b/src/class/audio/audio_device.c
@@ -1729,7 +1729,7 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const *
     // Find correct interface
     if (tu_desc_type(p_desc) == TUSB_DESC_INTERFACE && ((tusb_desc_interface_t const * )p_desc)->bInterfaceNumber == itf && ((tusb_desc_interface_t const * )p_desc)->bAlternateSetting == alt)
     {
-#if CFG_TUD_AUDIO_ENABLE_ENCODING || CFG_TUD_AUDIO_ENABLE_DECODING || CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL
+#if (CFG_TUD_AUDIO_ENABLE_EP_IN && (CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL || CFG_TUD_AUDIO_ENABLE_ENCODING)) || (CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_DECODING)
       uint8_t const * p_desc_parse_for_params = p_desc;
 #endif
       // From this point forward follow the EP descriptors associated to the current alternate setting interface - Open EPs if necessary
diff --git a/src/class/cdc/cdc.h b/src/class/cdc/cdc.h
index deec32ae4..5cbd658fe 100644
--- a/src/class/cdc/cdc.h
+++ b/src/class/cdc/cdc.h
@@ -136,8 +136,7 @@ typedef enum{
 //--------------------------------------------------------------------+
 
 /// Communication Interface Management Element Request Codes
-typedef enum
-{
+typedef enum {
   CDC_REQUEST_SEND_ENCAPSULATED_COMMAND                    = 0x00, ///< is used to issue a command in the format of the supported control protocol of the Communications Class interface
   CDC_REQUEST_GET_ENCAPSULATED_RESPONSE                    = 0x01, ///< is used to request a response in the format of the supported control protocol of the Communications Class interface.
   CDC_REQUEST_SET_COMM_FEATURE                             = 0x02,
@@ -180,39 +179,38 @@ typedef enum
   CDC_REQUEST_GET_ATM_VC_STATISTICS                        = 0x53,
 
   CDC_REQUEST_MDLM_SEMANTIC_MODEL                          = 0x60,
-}cdc_management_request_t;
+} cdc_management_request_t;
 
-enum {
+typedef enum {
   CDC_CONTROL_LINE_STATE_DTR = 0x01,
   CDC_CONTROL_LINE_STATE_RTS = 0x02,
-};
+} cdc_control_line_state_t;
 
-enum {
+typedef enum {
   CDC_LINE_CODING_STOP_BITS_1   = 0, // 1   bit
   CDC_LINE_CODING_STOP_BITS_1_5 = 1, // 1.5 bits
   CDC_LINE_CODING_STOP_BITS_2   = 2, // 2   bits
-};
+} cdc_line_coding_stopbits_t;
 
 // TODO Backward compatible for typos. Maybe removed in the future release
 #define CDC_LINE_CONDING_STOP_BITS_1   CDC_LINE_CODING_STOP_BITS_1
 #define CDC_LINE_CONDING_STOP_BITS_1_5 CDC_LINE_CODING_STOP_BITS_1_5
 #define CDC_LINE_CONDING_STOP_BITS_2   CDC_LINE_CODING_STOP_BITS_2
 
-enum {
+typedef enum {
   CDC_LINE_CODING_PARITY_NONE  = 0,
   CDC_LINE_CODING_PARITY_ODD   = 1,
   CDC_LINE_CODING_PARITY_EVEN  = 2,
   CDC_LINE_CODING_PARITY_MARK  = 3,
   CDC_LINE_CODING_PARITY_SPACE = 4,
-};
+} cdc_line_coding_parity_t;
 
 //--------------------------------------------------------------------+
 // Management Element Notification (Notification Endpoint)
 //--------------------------------------------------------------------+
 
 /// 6.3 Notification Codes
-typedef enum
-{
+typedef enum {
   CDC_NOTIF_NETWORK_CONNECTION               = 0x00, ///< This notification allows the device to notify the host about network connection status.
   CDC_NOTIF_RESPONSE_AVAILABLE               = 0x01, ///< This notification allows the device to notify the hostthat a response is available. This response can be retrieved with a subsequent \ref CDC_REQUEST_GET_ENCAPSULATED_RESPONSE request.
   CDC_NOTIF_AUX_JACK_HOOK_STATE              = 0x08,
diff --git a/src/class/cdc/cdc_host.c b/src/class/cdc/cdc_host.c
index a6dfb45ae..7adaa0c8c 100644
--- a/src/class/cdc/cdc_host.c
+++ b/src/class/cdc/cdc_host.c
@@ -22,6 +22,9 @@
  * THE SOFTWARE.
  *
  * This file is part of the TinyUSB stack.
+ *
+ * Contribution
+ * - Heiko Kuester: CH34x support
  */
 
 #include "tusb_option.h"
@@ -57,6 +60,11 @@ typedef struct {
   uint8_t line_state;                               // DTR (bit0), RTS (bit1)
   TU_ATTR_ALIGNED(4) cdc_line_coding_t line_coding; // Baudrate, stop bits, parity, data width
 
+  #if CFG_TUH_CDC_FTDI || CFG_TUH_CDC_CP210X || CFG_TUH_CDC_CH34X
+  cdc_line_coding_t requested_line_coding;
+  // 1 byte padding
+  #endif
+
   tuh_xfer_cb_t user_control_cb;
 
   struct {
@@ -69,7 +77,6 @@ typedef struct {
     uint8_t rx_ff_buf[CFG_TUH_CDC_TX_BUFSIZE];
     CFG_TUH_MEM_ALIGN uint8_t rx_ep_buf[CFG_TUH_CDC_TX_EPSIZE];
   } stream;
-
 } cdch_interface_t;
 
 CFG_TUH_MEM_SECTION
@@ -80,47 +87,60 @@ static cdch_interface_t cdch_data[CFG_TUH_CDC];
 //--------------------------------------------------------------------+
 
 //------------- ACM prototypes -------------//
+static bool acm_open(uint8_t daddr, tusb_desc_interface_t const *itf_desc, uint16_t max_len);
 static void acm_process_config(tuh_xfer_t* xfer);
 
+static bool acm_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+static bool acm_set_data_format(cdch_interface_t* p_cdc, uint8_t stop_bits, uint8_t parity, uint8_t data_bits, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
 static bool acm_set_line_coding(cdch_interface_t* p_cdc, cdc_line_coding_t const* line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
 static bool acm_set_control_line_state(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
-static bool acm_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
 
 //------------- FTDI prototypes -------------//
 #if CFG_TUH_CDC_FTDI
 #include "serial/ftdi_sio.h"
 
-static uint16_t const ftdi_pids[] = { CFG_TUH_CDC_FTDI_PID_LIST };
-enum {
-  FTDI_PID_COUNT = sizeof(ftdi_pids) / sizeof(ftdi_pids[0])
-};
-
-// Store last request baudrate since divisor to baudrate is not easy
-static uint32_t _ftdi_requested_baud;
+static uint16_t const ftdi_vid_pid_list[][2] = {CFG_TUH_CDC_FTDI_VID_PID_LIST};
 
 static bool ftdi_open(uint8_t daddr, const tusb_desc_interface_t *itf_desc, uint16_t max_len);
 static void ftdi_process_config(tuh_xfer_t* xfer);
 
-static bool ftdi_sio_set_modem_ctrl(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
 static bool ftdi_sio_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+static bool ftdi_set_data_format(cdch_interface_t* p_cdc, uint8_t stop_bits, uint8_t parity, uint8_t data_bits, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+static bool ftdi_set_line_coding(cdch_interface_t* p_cdc, cdc_line_coding_t const* line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+static bool ftdi_sio_set_modem_ctrl(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
 #endif
 
 //------------- CP210X prototypes -------------//
 #if CFG_TUH_CDC_CP210X
 #include "serial/cp210x.h"
 
-static uint16_t const cp210x_pids[] = { CFG_TUH_CDC_CP210X_PID_LIST };
-enum {
-  CP210X_PID_COUNT = sizeof(cp210x_pids) / sizeof(cp210x_pids[0])
-};
+static uint16_t const cp210x_vid_pid_list[][2] = {CFG_TUH_CDC_CP210X_VID_PID_LIST};
 
 static bool cp210x_open(uint8_t daddr, tusb_desc_interface_t const *itf_desc, uint16_t max_len);
 static void cp210x_process_config(tuh_xfer_t* xfer);
 
-static bool cp210x_set_modem_ctrl(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
 static bool cp210x_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+static bool cp210x_set_data_format(cdch_interface_t* p_cdc, uint8_t stop_bits, uint8_t parity, uint8_t data_bits, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+static bool cp210x_set_line_coding(cdch_interface_t* p_cdc, cdc_line_coding_t const* line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+static bool cp210x_set_modem_ctrl(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
 #endif
 
+//------------- CH34x prototypes -------------//
+#if CFG_TUH_CDC_CH34X
+#include "serial/ch34x.h"
+
+static uint16_t const ch34x_vid_pid_list[][2] = {CFG_TUH_CDC_CH34X_VID_PID_LIST};
+
+static bool ch34x_open(uint8_t daddr, tusb_desc_interface_t const* itf_desc, uint16_t max_len);
+static void ch34x_process_config(tuh_xfer_t* xfer);
+
+static bool ch34x_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+static bool ch34x_set_data_format(cdch_interface_t* p_cdc, uint8_t stop_bits, uint8_t parity, uint8_t data_bits, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+static bool ch34x_set_line_coding(cdch_interface_t* p_cdc, cdc_line_coding_t const* line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+static bool ch34x_set_modem_ctrl(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+#endif
+
+//------------- Common -------------//
 enum {
   SERIAL_DRIVER_ACM = 0,
 
@@ -131,60 +151,96 @@ enum {
 #if CFG_TUH_CDC_CP210X
   SERIAL_DRIVER_CP210X,
 #endif
+
+#if CFG_TUH_CDC_CH34X
+  SERIAL_DRIVER_CH34X,
+#endif
+
+  SERIAL_DRIVER_COUNT
 };
 
 typedef struct {
+  uint16_t const (*vid_pid_list)[2];
+  uint16_t const vid_pid_count;
+  bool (*const open)(uint8_t daddr, const tusb_desc_interface_t *itf_desc, uint16_t max_len);
   void (*const process_set_config)(tuh_xfer_t* xfer);
   bool (*const set_control_line_state)(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
   bool (*const set_baudrate)(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+  bool (*const set_data_format)(cdch_interface_t* p_cdc, uint8_t stop_bits, uint8_t parity, uint8_t data_bits, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+  bool (*const set_line_coding)(cdch_interface_t* p_cdc, cdc_line_coding_t const* line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
 } cdch_serial_driver_t;
 
 // Note driver list must be in the same order as SERIAL_DRIVER enum
 static const cdch_serial_driver_t serial_drivers[] = {
-  { .process_set_config     = acm_process_config,
-    .set_control_line_state = acm_set_control_line_state,
-    .set_baudrate           = acm_set_baudrate
+  {
+      .vid_pid_list           = NULL,
+      .vid_pid_count          = 0,
+      .open                   = acm_open,
+      .process_set_config     = acm_process_config,
+      .set_control_line_state = acm_set_control_line_state,
+      .set_baudrate           = acm_set_baudrate,
+      .set_data_format        = acm_set_data_format,
+      .set_line_coding        = acm_set_line_coding
   },
 
   #if CFG_TUH_CDC_FTDI
-  { .process_set_config     = ftdi_process_config,
-    .set_control_line_state = ftdi_sio_set_modem_ctrl,
-    .set_baudrate           = ftdi_sio_set_baudrate
+  {
+      .vid_pid_list           = ftdi_vid_pid_list,
+      .vid_pid_count          = TU_ARRAY_SIZE(ftdi_vid_pid_list),
+      .open                   = ftdi_open,
+      .process_set_config     = ftdi_process_config,
+      .set_control_line_state = ftdi_sio_set_modem_ctrl,
+      .set_baudrate           = ftdi_sio_set_baudrate,
+      .set_data_format        = ftdi_set_data_format,
+      .set_line_coding        = ftdi_set_line_coding
   },
   #endif
 
   #if CFG_TUH_CDC_CP210X
-  { .process_set_config     = cp210x_process_config,
-    .set_control_line_state = cp210x_set_modem_ctrl,
-    .set_baudrate           = cp210x_set_baudrate
+  {
+      .vid_pid_list           = cp210x_vid_pid_list,
+      .vid_pid_count          = TU_ARRAY_SIZE(cp210x_vid_pid_list),
+      .open                   = cp210x_open,
+      .process_set_config     = cp210x_process_config,
+      .set_control_line_state = cp210x_set_modem_ctrl,
+      .set_baudrate           = cp210x_set_baudrate,
+      .set_data_format        = cp210x_set_data_format,
+      .set_line_coding        = cp210x_set_line_coding
+  },
+  #endif
+
+  #if CFG_TUH_CDC_CH34X
+  {
+      .vid_pid_list           = ch34x_vid_pid_list,
+      .vid_pid_count          = TU_ARRAY_SIZE(ch34x_vid_pid_list),
+      .open                   = ch34x_open,
+      .process_set_config     = ch34x_process_config,
+      .set_control_line_state = ch34x_set_modem_ctrl,
+      .set_baudrate           = ch34x_set_baudrate,
+      .set_data_format        = ch34x_set_data_format,
+      .set_line_coding        = ch34x_set_line_coding
   },
   #endif
 };
 
-enum {
-  SERIAL_DRIVER_COUNT = sizeof(serial_drivers) / sizeof(serial_drivers[0])
-};
+TU_VERIFY_STATIC(TU_ARRAY_SIZE(serial_drivers) == SERIAL_DRIVER_COUNT, "Serial driver count mismatch");
 
 //--------------------------------------------------------------------+
 // INTERNAL OBJECT & FUNCTION DECLARATION
 //--------------------------------------------------------------------+
 
-static inline cdch_interface_t* get_itf(uint8_t idx)
-{
+static inline cdch_interface_t* get_itf(uint8_t idx) {
   TU_ASSERT(idx < CFG_TUH_CDC, NULL);
   cdch_interface_t* p_cdc = &cdch_data[idx];
 
   return (p_cdc->daddr != 0) ? p_cdc : NULL;
 }
 
-static inline uint8_t get_idx_by_ep_addr(uint8_t daddr, uint8_t ep_addr)
-{
-  for(uint8_t i=0; idaddr == daddr) &&
-         (ep_addr == p_cdc->ep_notif || ep_addr == p_cdc->stream.rx.ep_addr || ep_addr == p_cdc->stream.tx.ep_addr))
-    {
+         (ep_addr == p_cdc->ep_notif || ep_addr == p_cdc->stream.rx.ep_addr || ep_addr == p_cdc->stream.tx.ep_addr)) {
       return i;
     }
   }
@@ -192,14 +248,10 @@ static inline uint8_t get_idx_by_ep_addr(uint8_t daddr, uint8_t ep_addr)
   return TUSB_INDEX_INVALID_8;
 }
 
-
-static cdch_interface_t* make_new_itf(uint8_t daddr, tusb_desc_interface_t const *itf_desc)
-{
-  for(uint8_t i=0; idaddr              = daddr;
       p_cdc->bInterfaceNumber   = itf_desc->bInterfaceNumber;
       p_cdc->bInterfaceSubClass = itf_desc->bInterfaceSubClass;
@@ -220,20 +272,16 @@ static void cdch_internal_control_complete(tuh_xfer_t* xfer);
 // APPLICATION API
 //--------------------------------------------------------------------+
 
-uint8_t tuh_cdc_itf_get_index(uint8_t daddr, uint8_t itf_num)
-{
-  for(uint8_t i=0; idaddr == daddr && p_cdc->bInterfaceNumber == itf_num) return i;
   }
 
   return TUSB_INDEX_INVALID_8;
 }
 
-bool tuh_cdc_itf_get_info(uint8_t idx, tuh_itf_info_t* info)
-{
+bool tuh_cdc_itf_get_info(uint8_t idx, tuh_itf_info_t* info) {
   cdch_interface_t* p_cdc = get_itf(idx);
   TU_VERIFY(p_cdc && info);
 
@@ -255,30 +303,26 @@ bool tuh_cdc_itf_get_info(uint8_t idx, tuh_itf_info_t* info)
   return true;
 }
 
-bool tuh_cdc_mounted(uint8_t idx)
-{
+bool tuh_cdc_mounted(uint8_t idx) {
   cdch_interface_t* p_cdc = get_itf(idx);
   return p_cdc != NULL;
 }
 
-bool tuh_cdc_get_dtr(uint8_t idx)
-{
+bool tuh_cdc_get_dtr(uint8_t idx) {
   cdch_interface_t* p_cdc = get_itf(idx);
   TU_VERIFY(p_cdc);
 
   return (p_cdc->line_state & CDC_CONTROL_LINE_STATE_DTR) ? true : false;
 }
 
-bool tuh_cdc_get_rts(uint8_t idx)
-{
+bool tuh_cdc_get_rts(uint8_t idx) {
   cdch_interface_t* p_cdc = get_itf(idx);
   TU_VERIFY(p_cdc);
 
   return (p_cdc->line_state & CDC_CONTROL_LINE_STATE_RTS) ? true : false;
 }
 
-bool tuh_cdc_get_local_line_coding(uint8_t idx, cdc_line_coding_t* line_coding)
-{
+bool tuh_cdc_get_local_line_coding(uint8_t idx, cdc_line_coding_t* line_coding) {
   cdch_interface_t* p_cdc = get_itf(idx);
   TU_VERIFY(p_cdc);
 
@@ -291,32 +335,28 @@ bool tuh_cdc_get_local_line_coding(uint8_t idx, cdc_line_coding_t* line_coding)
 // Write
 //--------------------------------------------------------------------+
 
-uint32_t tuh_cdc_write(uint8_t idx, void const* buffer, uint32_t bufsize)
-{
+uint32_t tuh_cdc_write(uint8_t idx, void const* buffer, uint32_t bufsize) {
   cdch_interface_t* p_cdc = get_itf(idx);
   TU_VERIFY(p_cdc);
 
   return tu_edpt_stream_write(&p_cdc->stream.tx, buffer, bufsize);
 }
 
-uint32_t tuh_cdc_write_flush(uint8_t idx)
-{
+uint32_t tuh_cdc_write_flush(uint8_t idx) {
   cdch_interface_t* p_cdc = get_itf(idx);
   TU_VERIFY(p_cdc);
 
   return tu_edpt_stream_write_xfer(&p_cdc->stream.tx);
 }
 
-bool tuh_cdc_write_clear(uint8_t idx)
-{
+bool tuh_cdc_write_clear(uint8_t idx) {
   cdch_interface_t* p_cdc = get_itf(idx);
   TU_VERIFY(p_cdc);
 
   return tu_edpt_stream_clear(&p_cdc->stream.tx);
 }
 
-uint32_t tuh_cdc_write_available(uint8_t idx)
-{
+uint32_t tuh_cdc_write_available(uint8_t idx) {
   cdch_interface_t* p_cdc = get_itf(idx);
   TU_VERIFY(p_cdc);
 
@@ -327,32 +367,28 @@ uint32_t tuh_cdc_write_available(uint8_t idx)
 // Read
 //--------------------------------------------------------------------+
 
-uint32_t tuh_cdc_read (uint8_t idx, void* buffer, uint32_t bufsize)
-{
+uint32_t tuh_cdc_read (uint8_t idx, void* buffer, uint32_t bufsize) {
   cdch_interface_t* p_cdc = get_itf(idx);
   TU_VERIFY(p_cdc);
 
   return tu_edpt_stream_read(&p_cdc->stream.rx, buffer, bufsize);
 }
 
-uint32_t tuh_cdc_read_available(uint8_t idx)
-{
+uint32_t tuh_cdc_read_available(uint8_t idx) {
   cdch_interface_t* p_cdc = get_itf(idx);
   TU_VERIFY(p_cdc);
 
   return tu_edpt_stream_read_available(&p_cdc->stream.rx);
 }
 
-bool tuh_cdc_peek(uint8_t idx, uint8_t* ch)
-{
+bool tuh_cdc_peek(uint8_t idx, uint8_t* ch) {
   cdch_interface_t* p_cdc = get_itf(idx);
   TU_VERIFY(p_cdc);
 
   return tu_edpt_stream_peek(&p_cdc->stream.rx, ch);
 }
 
-bool tuh_cdc_read_clear (uint8_t idx)
-{
+bool tuh_cdc_read_clear (uint8_t idx) {
   cdch_interface_t* p_cdc = get_itf(idx);
   TU_VERIFY(p_cdc);
 
@@ -365,28 +401,25 @@ bool tuh_cdc_read_clear (uint8_t idx)
 // Control Endpoint API
 //--------------------------------------------------------------------+
 
-// internal control complete to update state such as line state, encoding
-static void cdch_internal_control_complete(tuh_xfer_t* xfer)
-{
-  uint8_t const itf_num = (uint8_t) tu_le16toh(xfer->setup->wIndex);
+static void process_internal_control_complete(tuh_xfer_t* xfer, uint8_t itf_num) {
   uint8_t idx = tuh_cdc_itf_get_index(xfer->daddr, itf_num);
   cdch_interface_t* p_cdc = get_itf(idx);
   TU_ASSERT(p_cdc, );
+  uint16_t const value = tu_le16toh(xfer->setup->wValue);
 
-  if (xfer->result == XFER_RESULT_SUCCESS)
-  {
+  if (xfer->result == XFER_RESULT_SUCCESS) {
     switch (p_cdc->serial_drid) {
       case SERIAL_DRIVER_ACM:
         switch (xfer->setup->bRequest) {
           case CDC_REQUEST_SET_CONTROL_LINE_STATE:
-            p_cdc->line_state = (uint8_t) tu_le16toh(xfer->setup->wValue);
+            p_cdc->line_state = (uint8_t) value;
             break;
 
           case CDC_REQUEST_SET_LINE_CODING: {
             uint16_t const len = tu_min16(sizeof(cdc_line_coding_t), tu_le16toh(xfer->setup->wLength));
             memcpy(&p_cdc->line_coding, xfer->buffer, len);
-          }
             break;
+          }
 
           default: break;
         }
@@ -396,12 +429,11 @@ static void cdch_internal_control_complete(tuh_xfer_t* xfer)
       case SERIAL_DRIVER_FTDI:
         switch (xfer->setup->bRequest) {
           case FTDI_SIO_MODEM_CTRL:
-            p_cdc->line_state = (uint8_t) (tu_le16toh(xfer->setup->wValue) & 0x00ff);
+            p_cdc->line_state = (uint8_t) value;
             break;
 
           case FTDI_SIO_SET_BAUD_RATE:
-            // convert from divisor to baudrate is not supported
-            p_cdc->line_coding.bit_rate = _ftdi_requested_baud;
+            p_cdc->line_coding.bit_rate = p_cdc->requested_line_coding.bit_rate;
             break;
 
           default: break;
@@ -413,15 +445,61 @@ static void cdch_internal_control_complete(tuh_xfer_t* xfer)
       case SERIAL_DRIVER_CP210X:
         switch(xfer->setup->bRequest) {
           case CP210X_SET_MHS:
-            p_cdc->line_state = (uint8_t) (tu_le16toh(xfer->setup->wValue) & 0x00ff);
+            p_cdc->line_state = (uint8_t) value;
             break;
 
           case CP210X_SET_BAUDRATE: {
             uint32_t baudrate;
             memcpy(&baudrate, xfer->buffer, sizeof(uint32_t));
             p_cdc->line_coding.bit_rate = tu_le32toh(baudrate);
-          }
             break;
+          }
+
+          default: break;
+        }
+        break;
+      #endif
+
+      #if CFG_TUH_CDC_CH34X
+      case SERIAL_DRIVER_CH34X:
+        switch (xfer->setup->bRequest) {
+          case CH34X_REQ_WRITE_REG:
+            // register write request
+            switch (value) {
+              case CH34X_REG16_DIVISOR_PRESCALER:
+                // baudrate
+                p_cdc->line_coding.bit_rate = p_cdc->requested_line_coding.bit_rate;
+                break;
+
+              case CH32X_REG16_LCR2_LCR:
+                // data format
+                p_cdc->line_coding.stop_bits = p_cdc->requested_line_coding.stop_bits;
+                p_cdc->line_coding.parity = p_cdc->requested_line_coding.parity;
+                p_cdc->line_coding.data_bits = p_cdc->requested_line_coding.data_bits;
+                break;
+
+              default: break;
+            }
+            break;
+
+          case CH34X_REQ_MODEM_CTRL: {
+            // set modem controls RTS/DTR request. Note: signals are inverted
+            uint16_t const modem_signal = ~value;
+            if (modem_signal & CH34X_BIT_RTS) {
+              p_cdc->line_state |= CDC_CONTROL_LINE_STATE_RTS;
+            } else {
+              p_cdc->line_state &= (uint8_t) ~CDC_CONTROL_LINE_STATE_RTS;
+            }
+
+            if (modem_signal & CH34X_BIT_DTR) {
+              p_cdc->line_state |= CDC_CONTROL_LINE_STATE_DTR;
+            } else {
+              p_cdc->line_state &= (uint8_t) ~CDC_CONTROL_LINE_STATE_DTR;
+            }
+            break;
+          }
+
+          default: break;
         }
         break;
       #endif
@@ -436,14 +514,20 @@ static void cdch_internal_control_complete(tuh_xfer_t* xfer)
   }
 }
 
+// internal control complete to update state such as line state, encoding
+static void cdch_internal_control_complete(tuh_xfer_t* xfer) {
+  uint8_t const itf_num = (uint8_t) tu_le16toh(xfer->setup->wIndex);
+  process_internal_control_complete(xfer, itf_num);
+}
+
 bool tuh_cdc_set_control_line_state(uint8_t idx, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
   cdch_interface_t* p_cdc = get_itf(idx);
   TU_VERIFY(p_cdc && p_cdc->serial_drid < SERIAL_DRIVER_COUNT);
   cdch_serial_driver_t const* driver = &serial_drivers[p_cdc->serial_drid];
 
-  if ( complete_cb ) {
+  if (complete_cb) {
     return driver->set_control_line_state(p_cdc, line_state, complete_cb, user_data);
-  }else {
+  } else {
     // blocking
     xfer_result_t result = XFER_RESULT_INVALID;
     bool ret = driver->set_control_line_state(p_cdc, line_state, complete_cb, (uintptr_t) &result);
@@ -454,7 +538,6 @@ bool tuh_cdc_set_control_line_state(uint8_t idx, uint16_t line_state, tuh_xfer_c
     }
 
     TU_VERIFY(ret && result == XFER_RESULT_SUCCESS);
-
     p_cdc->line_state = (uint8_t) line_state;
     return true;
   }
@@ -465,9 +548,9 @@ bool tuh_cdc_set_baudrate(uint8_t idx, uint32_t baudrate, tuh_xfer_cb_t complete
   TU_VERIFY(p_cdc && p_cdc->serial_drid < SERIAL_DRIVER_COUNT);
   cdch_serial_driver_t const* driver = &serial_drivers[p_cdc->serial_drid];
 
-  if ( complete_cb ) {
+  if (complete_cb) {
     return driver->set_baudrate(p_cdc, baudrate, complete_cb, user_data);
-  }else {
+  } else {
     // blocking
     xfer_result_t result = XFER_RESULT_INVALID;
     bool ret = driver->set_baudrate(p_cdc, baudrate, complete_cb, (uintptr_t) &result);
@@ -478,25 +561,23 @@ bool tuh_cdc_set_baudrate(uint8_t idx, uint32_t baudrate, tuh_xfer_cb_t complete
     }
 
     TU_VERIFY(ret && result == XFER_RESULT_SUCCESS);
-
     p_cdc->line_coding.bit_rate = baudrate;
     return true;
   }
 }
 
-bool tuh_cdc_set_line_coding(uint8_t idx, cdc_line_coding_t const* line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data)
-{
+bool tuh_cdc_set_data_format(uint8_t idx, uint8_t stop_bits, uint8_t parity, uint8_t data_bits,
+                             tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
   cdch_interface_t* p_cdc = get_itf(idx);
-  // only ACM support this set line coding request
-  TU_VERIFY(p_cdc && p_cdc->serial_drid == SERIAL_DRIVER_ACM);
-  TU_VERIFY(p_cdc->acm_capability.support_line_request);
+  TU_VERIFY(p_cdc && p_cdc->serial_drid < SERIAL_DRIVER_COUNT);
+  cdch_serial_driver_t const* driver = &serial_drivers[p_cdc->serial_drid];
 
-  if ( complete_cb ) {
-    return acm_set_line_coding(p_cdc, line_coding, complete_cb, user_data);
-  }else {
+  if (complete_cb) {
+    return driver->set_data_format(p_cdc, stop_bits, parity, data_bits, complete_cb, user_data);
+  } else {
     // blocking
     xfer_result_t result = XFER_RESULT_INVALID;
-    bool ret = acm_set_line_coding(p_cdc, line_coding, complete_cb, (uintptr_t) &result);
+    bool ret = driver->set_data_format(p_cdc, stop_bits, parity, data_bits, complete_cb, (uintptr_t) &result);
 
     if (user_data) {
       // user_data is not NULL, return result via user_data
@@ -504,7 +585,31 @@ bool tuh_cdc_set_line_coding(uint8_t idx, cdc_line_coding_t const* line_coding,
     }
 
     TU_VERIFY(ret && result == XFER_RESULT_SUCCESS);
+    p_cdc->line_coding.stop_bits = stop_bits;
+    p_cdc->line_coding.parity = parity;
+    p_cdc->line_coding.data_bits = data_bits;
+    return true;
+  }
+}
 
+bool tuh_cdc_set_line_coding(uint8_t idx, cdc_line_coding_t const* line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+  cdch_interface_t* p_cdc = get_itf(idx);
+  TU_VERIFY(p_cdc && p_cdc->serial_drid < SERIAL_DRIVER_COUNT);
+  cdch_serial_driver_t const* driver = &serial_drivers[p_cdc->serial_drid];
+
+  if ( complete_cb ) {
+    return driver->set_line_coding(p_cdc, line_coding, complete_cb, user_data);
+  } else {
+    // blocking
+    xfer_result_t result = XFER_RESULT_INVALID;
+    bool ret = driver->set_line_coding(p_cdc, line_coding, complete_cb, (uintptr_t) &result);
+
+    if (user_data) {
+      // user_data is not NULL, return result via user_data
+      *((xfer_result_t*) user_data) = result;
+    }
+
+    TU_VERIFY(ret && result == XFER_RESULT_SUCCESS);
     p_cdc->line_coding = *line_coding;
     return true;
   }
@@ -514,31 +619,26 @@ bool tuh_cdc_set_line_coding(uint8_t idx, cdc_line_coding_t const* line_coding,
 // CLASS-USBH API
 //--------------------------------------------------------------------+
 
-void cdch_init(void)
-{
+void cdch_init(void) {
   tu_memclr(cdch_data, sizeof(cdch_data));
 
-  for(size_t i=0; istream.tx, true, true, false,
-                          p_cdc->stream.tx_ff_buf, CFG_TUH_CDC_TX_BUFSIZE,
-                          p_cdc->stream.tx_ep_buf, CFG_TUH_CDC_TX_EPSIZE);
+                        p_cdc->stream.tx_ff_buf, CFG_TUH_CDC_TX_BUFSIZE,
+                        p_cdc->stream.tx_ep_buf, CFG_TUH_CDC_TX_EPSIZE);
 
     tu_edpt_stream_init(&p_cdc->stream.rx, true, false, false,
-                          p_cdc->stream.rx_ff_buf, CFG_TUH_CDC_RX_BUFSIZE,
-                          p_cdc->stream.rx_ep_buf, CFG_TUH_CDC_RX_EPSIZE);
+                        p_cdc->stream.rx_ff_buf, CFG_TUH_CDC_RX_BUFSIZE,
+                        p_cdc->stream.rx_ep_buf, CFG_TUH_CDC_RX_EPSIZE);
   }
 }
 
-void cdch_close(uint8_t daddr)
-{
-  for(uint8_t idx=0; idxdaddr == daddr)
-    {
+    if (p_cdc->daddr == daddr) {
       TU_LOG_DRV("  CDCh close addr = %u index = %u\r\n", daddr, idx);
 
       // Invoke application callback
@@ -570,16 +670,11 @@ bool cdch_xfer_cb(uint8_t daddr, uint8_t ep_addr, xfer_result_t event, uint32_t
       // - xferred_bytes is multiple of EP Packet size and not zero
       tu_edpt_stream_write_zlp_if_needed(&p_cdc->stream.tx, xferred_bytes);
     }
-  }
-  else if ( ep_addr == p_cdc->stream.rx.ep_addr ) {
+  } else if ( ep_addr == p_cdc->stream.rx.ep_addr ) {
     #if CFG_TUH_CDC_FTDI
     if (p_cdc->serial_drid == SERIAL_DRIVER_FTDI) {
       // FTDI reserve 2 bytes for status
-      // FTDI status
-//      uint8_t status[2] = {
-//        p_cdc->stream.rx.ep_buf[0],
-//        p_cdc->stream.rx.ep_buf[1]
-//      };
+      // uint8_t status[2] = {p_cdc->stream.rx.ep_buf[0], p_cdc->stream.rx.ep_buf[1]};
       tu_edpt_stream_read_xfer_complete_offset(&p_cdc->stream.rx, xferred_bytes, 2);
     }else
     #endif
@@ -605,22 +700,15 @@ bool cdch_xfer_cb(uint8_t daddr, uint8_t ep_addr, xfer_result_t event, uint32_t
 // Enumeration
 //--------------------------------------------------------------------+
 
-static bool acm_open(uint8_t daddr, tusb_desc_interface_t const *itf_desc, uint16_t max_len);
-
-static bool open_ep_stream_pair(cdch_interface_t* p_cdc, tusb_desc_endpoint_t const *desc_ep)
-{
-  for(size_t i=0; i<2; i++)
-  {
+static bool open_ep_stream_pair(cdch_interface_t* p_cdc, tusb_desc_endpoint_t const* desc_ep) {
+  for (size_t i = 0; i < 2; i++) {
     TU_ASSERT(TUSB_DESC_ENDPOINT == desc_ep->bDescriptorType &&
-              TUSB_XFER_BULK     == desc_ep->bmAttributes.xfer);
-
+              TUSB_XFER_BULK == desc_ep->bmAttributes.xfer);
     TU_ASSERT(tuh_edpt_open(p_cdc->daddr, desc_ep));
 
-    if ( tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_IN )
-    {
+    if (tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_IN) {
       tu_edpt_stream_open(&p_cdc->stream.rx, p_cdc->daddr, desc_ep);
-    }else
-    {
+    } else {
       tu_edpt_stream_open(&p_cdc->stream.tx, p_cdc->daddr, desc_ep);
     }
 
@@ -630,49 +718,35 @@ static bool open_ep_stream_pair(cdch_interface_t* p_cdc, tusb_desc_endpoint_t co
   return true;
 }
 
-bool cdch_open(uint8_t rhport, uint8_t daddr, tusb_desc_interface_t const *itf_desc, uint16_t max_len)
-{
+bool cdch_open(uint8_t rhport, uint8_t daddr, tusb_desc_interface_t const *itf_desc, uint16_t max_len) {
   (void) rhport;
 
-  // Only support ACM subclass
+  // For CDC: only support ACM subclass
   // Note: Protocol 0xFF can be RNDIS device
-  if ( TUSB_CLASS_CDC                           == itf_desc->bInterfaceClass &&
-       CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL == itf_desc->bInterfaceSubClass)
-  {
+  if (TUSB_CLASS_CDC                           == itf_desc->bInterfaceClass &&
+      CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL == itf_desc->bInterfaceSubClass) {
     return acm_open(daddr, itf_desc, max_len);
   }
-  #if CFG_TUH_CDC_FTDI || CFG_TUH_CDC_CP210X
-  else if ( 0xff == itf_desc->bInterfaceClass )
-  {
+  else if (SERIAL_DRIVER_COUNT > 1 &&
+           TUSB_CLASS_VENDOR_SPECIFIC == itf_desc->bInterfaceClass) {
     uint16_t vid, pid;
     TU_VERIFY(tuh_vid_pid_get(daddr, &vid, &pid));
 
-    #if CFG_TUH_CDC_FTDI
-    if (TU_FTDI_VID == vid) {
-      for (size_t i = 0; i < FTDI_PID_COUNT; i++) {
-        if (ftdi_pids[i] == pid) {
-          return ftdi_open(daddr, itf_desc, max_len);
+    for (size_t dr = 1; dr < SERIAL_DRIVER_COUNT; dr++) {
+      cdch_serial_driver_t const* driver = &serial_drivers[dr];
+      for (size_t i = 0; i < driver->vid_pid_count; i++) {
+        if (driver->vid_pid_list[i][0] == vid && driver->vid_pid_list[i][1] == pid) {
+          return driver->open(daddr, itf_desc, max_len);
         }
       }
     }
-    #endif
-
-    #if CFG_TUH_CDC_CP210X
-    if (TU_CP210X_VID == vid) {
-      for (size_t i = 0; i < CP210X_PID_COUNT; i++) {
-        if (cp210x_pids[i] == pid) {
-          return cp210x_open(daddr, itf_desc, max_len);
-        }
-      }
-    }
-    #endif
   }
-  #endif
 
   return false;
 }
 
 static void set_config_complete(cdch_interface_t * p_cdc, uint8_t idx, uint8_t itf_num) {
+  TU_LOG_DRV("CDCh Set Configure complete\r\n");
   if (tuh_cdc_mount_cb) tuh_cdc_mount_cb(idx);
 
   // Prepare for incoming data
@@ -682,9 +756,7 @@ static void set_config_complete(cdch_interface_t * p_cdc, uint8_t idx, uint8_t i
   usbh_driver_set_config_complete(p_cdc->daddr, itf_num);
 }
 
-
-bool cdch_set_config(uint8_t daddr, uint8_t itf_num)
-{
+bool cdch_set_config(uint8_t daddr, uint8_t itf_num) {
   tusb_control_request_t request;
   request.wIndex = tu_htole16((uint16_t) itf_num);
 
@@ -713,94 +785,84 @@ enum {
   CONFIG_ACM_COMPLETE,
 };
 
-static bool acm_open(uint8_t daddr, tusb_desc_interface_t const *itf_desc, uint16_t max_len)
-{
-  uint8_t const * p_desc_end = ((uint8_t const*) itf_desc) + max_len;
+static bool acm_open(uint8_t daddr, tusb_desc_interface_t const* itf_desc, uint16_t max_len) {
+  uint8_t const* p_desc_end = ((uint8_t const*) itf_desc) + max_len;
 
-  cdch_interface_t * p_cdc = make_new_itf(daddr, itf_desc);
+  cdch_interface_t* p_cdc = make_new_itf(daddr, itf_desc);
   TU_VERIFY(p_cdc);
-
   p_cdc->serial_drid = SERIAL_DRIVER_ACM;
 
   //------------- Control Interface -------------//
-  uint8_t const * p_desc = tu_desc_next(itf_desc);
+  uint8_t const* p_desc = tu_desc_next(itf_desc);
 
   // Communication Functional Descriptors
-  while( (p_desc < p_desc_end) && (TUSB_DESC_CS_INTERFACE == tu_desc_type(p_desc)) )
-  {
-    if ( CDC_FUNC_DESC_ABSTRACT_CONTROL_MANAGEMENT == cdc_functional_desc_typeof(p_desc) )
-    {
+  while ((p_desc < p_desc_end) && (TUSB_DESC_CS_INTERFACE == tu_desc_type(p_desc))) {
+    if (CDC_FUNC_DESC_ABSTRACT_CONTROL_MANAGEMENT == cdc_functional_desc_typeof(p_desc)) {
       // save ACM bmCapabilities
-      p_cdc->acm_capability = ((cdc_desc_func_acm_t const *) p_desc)->bmCapabilities;
+      p_cdc->acm_capability = ((cdc_desc_func_acm_t const*) p_desc)->bmCapabilities;
     }
 
     p_desc = tu_desc_next(p_desc);
   }
 
   // Open notification endpoint of control interface if any
-  if (itf_desc->bNumEndpoints == 1)
-  {
+  if (itf_desc->bNumEndpoints == 1) {
     TU_ASSERT(TUSB_DESC_ENDPOINT == tu_desc_type(p_desc));
-    tusb_desc_endpoint_t const * desc_ep = (tusb_desc_endpoint_t const *) p_desc;
+    tusb_desc_endpoint_t const* desc_ep = (tusb_desc_endpoint_t const*) p_desc;
 
-    TU_ASSERT( tuh_edpt_open(daddr, desc_ep) );
+    TU_ASSERT(tuh_edpt_open(daddr, desc_ep));
     p_cdc->ep_notif = desc_ep->bEndpointAddress;
 
     p_desc = tu_desc_next(p_desc);
   }
 
   //------------- Data Interface (if any) -------------//
-  if ( (TUSB_DESC_INTERFACE == tu_desc_type(p_desc)) &&
-       (TUSB_CLASS_CDC_DATA == ((tusb_desc_interface_t const *) p_desc)->bInterfaceClass) )
-  {
+  if ((TUSB_DESC_INTERFACE == tu_desc_type(p_desc)) &&
+      (TUSB_CLASS_CDC_DATA == ((tusb_desc_interface_t const*) p_desc)->bInterfaceClass)) {
     // next to endpoint descriptor
     p_desc = tu_desc_next(p_desc);
 
     // data endpoints expected to be in pairs
-    TU_ASSERT(open_ep_stream_pair(p_cdc, (tusb_desc_endpoint_t const *) p_desc));
+    TU_ASSERT(open_ep_stream_pair(p_cdc, (tusb_desc_endpoint_t const*) p_desc));
   }
 
   return true;
 }
 
-static void acm_process_config(tuh_xfer_t* xfer)
-{
+static void acm_process_config(tuh_xfer_t* xfer) {
   uintptr_t const state = xfer->user_data;
   uint8_t const itf_num = (uint8_t) tu_le16toh(xfer->setup->wIndex);
   uint8_t const idx = tuh_cdc_itf_get_index(xfer->daddr, itf_num);
-  cdch_interface_t * p_cdc = get_itf(idx);
-  TU_ASSERT(p_cdc, );
+  cdch_interface_t* p_cdc = get_itf(idx);
+  TU_ASSERT(p_cdc,);
 
-  switch(state)
-  {
+  switch (state) {
     case CONFIG_ACM_SET_CONTROL_LINE_STATE:
       #if CFG_TUH_CDC_LINE_CONTROL_ON_ENUM
-      if (p_cdc->acm_capability.support_line_request)
-      {
-        TU_ASSERT(acm_set_control_line_state(p_cdc, CFG_TUH_CDC_LINE_CONTROL_ON_ENUM, acm_process_config,
-                                             CONFIG_ACM_SET_LINE_CODING), );
+      if (p_cdc->acm_capability.support_line_request) {
+        TU_ASSERT(acm_set_control_line_state(p_cdc, CFG_TUH_CDC_LINE_CONTROL_ON_ENUM, acm_process_config, CONFIG_ACM_SET_LINE_CODING),);
         break;
       }
-          #endif
+      #endif
       TU_ATTR_FALLTHROUGH;
 
     case CONFIG_ACM_SET_LINE_CODING:
-        #ifdef CFG_TUH_CDC_LINE_CODING_ON_ENUM
-      if (p_cdc->acm_capability.support_line_request)
-      {
+      #ifdef CFG_TUH_CDC_LINE_CODING_ON_ENUM
+      if (p_cdc->acm_capability.support_line_request) {
         cdc_line_coding_t line_coding = CFG_TUH_CDC_LINE_CODING_ON_ENUM;
-        TU_ASSERT(acm_set_line_coding(p_cdc, &line_coding, acm_process_config, CONFIG_ACM_COMPLETE), );
+        TU_ASSERT(acm_set_line_coding(p_cdc, &line_coding, acm_process_config, CONFIG_ACM_COMPLETE),);
         break;
       }
-        #endif
+      #endif
       TU_ATTR_FALLTHROUGH;
 
     case CONFIG_ACM_COMPLETE:
       // itf_num+1 to account for data interface as well
-      set_config_complete(p_cdc, idx, itf_num+1);
+      set_config_complete(p_cdc, idx, itf_num + 1);
       break;
 
-    default: break;
+    default:
+      break;
   }
 }
 
@@ -868,6 +930,19 @@ static bool acm_set_line_coding(cdch_interface_t* p_cdc, cdc_line_coding_t const
   return true;
 }
 
+static bool acm_set_data_format(cdch_interface_t* p_cdc, uint8_t stop_bits, uint8_t parity, uint8_t data_bits,
+                                tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+  TU_LOG_DRV("CDC ACM Set Data Format\r\n");
+
+  cdc_line_coding_t line_coding;
+  line_coding.bit_rate = p_cdc->line_coding.bit_rate;
+  line_coding.stop_bits = stop_bits;
+  line_coding.parity = parity;
+  line_coding.data_bits = data_bits;
+
+  return acm_set_line_coding(p_cdc, &line_coding, complete_cb, user_data);
+}
+
 static bool acm_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
   TU_VERIFY(p_cdc->acm_capability.support_line_request);
   cdc_line_coding_t line_coding = p_cdc->line_coding;
@@ -897,7 +972,6 @@ static bool ftdi_open(uint8_t daddr, const tusb_desc_interface_t *itf_desc, uint
   TU_VERIFY(p_cdc);
 
   TU_LOG_DRV("FTDI opened\r\n");
-
   p_cdc->serial_drid = SERIAL_DRIVER_FTDI;
 
   // endpoint pair
@@ -933,13 +1007,32 @@ static bool ftdi_sio_set_request(cdch_interface_t* p_cdc, uint8_t command, uint1
   return tuh_control_xfer(&xfer);
 }
 
-static bool ftdi_sio_reset(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data)
-{
+static bool ftdi_sio_reset(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
   return ftdi_sio_set_request(p_cdc, FTDI_SIO_RESET, FTDI_SIO_RESET_SIO, complete_cb, user_data);
 }
 
-static bool ftdi_sio_set_modem_ctrl(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data)
-{
+static bool ftdi_set_data_format(cdch_interface_t* p_cdc, uint8_t stop_bits, uint8_t parity, uint8_t data_bits,
+                                 tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+  (void) p_cdc;
+  (void) stop_bits;
+  (void) parity;
+  (void) data_bits;
+  (void) complete_cb;
+  (void) user_data;
+  // TODO not implemented yet
+  return false;
+}
+
+static bool ftdi_set_line_coding(cdch_interface_t* p_cdc, cdc_line_coding_t const* line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+  (void) p_cdc;
+  (void) line_coding;
+  (void) complete_cb;
+  (void) user_data;
+  // TODO not implemented yet
+  return false;
+}
+
+static bool ftdi_sio_set_modem_ctrl(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
   TU_LOG_DRV("CDC FTDI Set Control Line State\r\n");
   p_cdc->user_control_cb = complete_cb;
   TU_ASSERT(ftdi_sio_set_request(p_cdc, FTDI_SIO_MODEM_CTRL, 0x0300 | line_state,
@@ -947,8 +1040,7 @@ static bool ftdi_sio_set_modem_ctrl(cdch_interface_t* p_cdc, uint16_t line_state
   return true;
 }
 
-static uint32_t ftdi_232bm_baud_base_to_divisor(uint32_t baud, uint32_t base)
-{
+static uint32_t ftdi_232bm_baud_base_to_divisor(uint32_t baud, uint32_t base) {
   const uint8_t divfrac[8] = { 0, 3, 2, 4, 1, 5, 6, 7 };
   uint32_t divisor;
 
@@ -968,18 +1060,16 @@ static uint32_t ftdi_232bm_baud_base_to_divisor(uint32_t baud, uint32_t base)
   return divisor;
 }
 
-static uint32_t ftdi_232bm_baud_to_divisor(uint32_t baud)
-{
+static uint32_t ftdi_232bm_baud_to_divisor(uint32_t baud) {
   return ftdi_232bm_baud_base_to_divisor(baud, 48000000u);
 }
 
-static bool ftdi_sio_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data)
-{
+static bool ftdi_sio_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
   uint16_t const divisor = (uint16_t) ftdi_232bm_baud_to_divisor(baudrate);
   TU_LOG_DRV("CDC FTDI Set BaudRate = %lu, divisor = 0x%04x\r\n", baudrate, divisor);
 
   p_cdc->user_control_cb = complete_cb;
-  _ftdi_requested_baud = baudrate;
+  p_cdc->requested_line_coding.bit_rate = baudrate;
   TU_ASSERT(ftdi_sio_set_request(p_cdc, FTDI_SIO_SET_BAUD_RATE, divisor,
                                  complete_cb ? cdch_internal_control_complete : NULL, user_data));
 
@@ -1001,8 +1091,7 @@ static void ftdi_process_config(tuh_xfer_t* xfer) {
 
     case CONFIG_FTDI_MODEM_CTRL:
       #if CFG_TUH_CDC_LINE_CONTROL_ON_ENUM
-      TU_ASSERT(
-        ftdi_sio_set_modem_ctrl(p_cdc, CFG_TUH_CDC_LINE_CONTROL_ON_ENUM, ftdi_process_config, CONFIG_FTDI_SET_BAUDRATE),);
+      TU_ASSERT(ftdi_sio_set_modem_ctrl(p_cdc, CFG_TUH_CDC_LINE_CONTROL_ON_ENUM, ftdi_process_config, CONFIG_FTDI_SET_BAUDRATE),);
       break;
       #else
       TU_ATTR_FALLTHROUGH;
@@ -1110,6 +1199,15 @@ static bool cp210x_ifc_enable(cdch_interface_t* p_cdc, uint16_t enabled, tuh_xfe
   return cp210x_set_request(p_cdc, CP210X_IFC_ENABLE, enabled, NULL, 0, complete_cb, user_data);
 }
 
+static bool cp210x_set_line_coding(cdch_interface_t* p_cdc, cdc_line_coding_t const* line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+  // TODO implement later
+  (void) p_cdc;
+  (void) line_coding;
+  (void) complete_cb;
+  (void) user_data;
+  return false;
+}
+
 static bool cp210x_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
   TU_LOG_DRV("CDC CP210x Set BaudRate = %lu\r\n", baudrate);
   uint32_t baud_le = tu_htole32(baudrate);
@@ -1118,8 +1216,19 @@ static bool cp210x_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_
                             complete_cb ? cdch_internal_control_complete : NULL, user_data);
 }
 
-static bool cp210x_set_modem_ctrl(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data)
-{
+static bool cp210x_set_data_format(cdch_interface_t* p_cdc, uint8_t stop_bits, uint8_t parity, uint8_t data_bits,
+                                   tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+  (void) p_cdc;
+  (void) stop_bits;
+  (void) parity;
+  (void) data_bits;
+  (void) complete_cb;
+  (void) user_data;
+  // TODO not implemented yet
+  return false;
+}
+
+static bool cp210x_set_modem_ctrl(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
   TU_LOG_DRV("CDC CP210x Set Control Line State\r\n");
   p_cdc->user_control_cb = complete_cb;
   return cp210x_set_request(p_cdc, CP210X_SET_MHS, 0x0300 | line_state, NULL, 0,
@@ -1159,8 +1268,7 @@ static void cp210x_process_config(tuh_xfer_t* xfer) {
 
     case CONFIG_CP210X_SET_DTR_RTS:
       #if CFG_TUH_CDC_LINE_CONTROL_ON_ENUM
-      TU_ASSERT(
-        cp210x_set_modem_ctrl(p_cdc, CFG_TUH_CDC_LINE_CONTROL_ON_ENUM, cp210x_process_config, CONFIG_CP210X_COMPLETE),);
+      TU_ASSERT(cp210x_set_modem_ctrl(p_cdc, CFG_TUH_CDC_LINE_CONTROL_ON_ENUM, cp210x_process_config, CONFIG_CP210X_COMPLETE),);
       break;
       #else
       TU_ATTR_FALLTHROUGH;
@@ -1176,4 +1284,374 @@ static void cp210x_process_config(tuh_xfer_t* xfer) {
 
 #endif
 
+//--------------------------------------------------------------------+
+// CH34x (CH340 & CH341)
+//--------------------------------------------------------------------+
+
+#if CFG_TUH_CDC_CH34X
+
+static uint8_t ch34x_get_lcr(uint8_t stop_bits, uint8_t parity, uint8_t data_bits);
+static uint16_t ch34x_get_divisor_prescaler(uint32_t baval);
+
+//------------- control request -------------//
+
+static bool ch34x_set_request(cdch_interface_t* p_cdc, uint8_t direction, uint8_t request, uint16_t value,
+                              uint16_t index, uint8_t* buffer, uint16_t length, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+  tusb_control_request_t const request_setup = {
+      .bmRequestType_bit = {
+          .recipient = TUSB_REQ_RCPT_DEVICE,
+          .type      = TUSB_REQ_TYPE_VENDOR,
+          .direction = direction & 0x01u
+      },
+      .bRequest = request,
+      .wValue   = tu_htole16 (value),
+      .wIndex   = tu_htole16 (index),
+      .wLength  = tu_htole16 (length)
+  };
+
+  // use usbh enum buf since application variable does not live long enough
+  uint8_t* enum_buf = NULL;
+
+  if (buffer && length > 0) {
+    enum_buf = usbh_get_enum_buf();
+    if (direction == TUSB_DIR_OUT) {
+      tu_memcpy_s(enum_buf, CFG_TUH_ENUMERATION_BUFSIZE, buffer, length);
+    }
+  }
+
+  tuh_xfer_t xfer = {
+      .daddr       = p_cdc->daddr,
+      .ep_addr     = 0,
+      .setup       = &request_setup,
+      .buffer      = enum_buf,
+      .complete_cb = complete_cb,
+      .user_data   = user_data
+  };
+
+  return tuh_control_xfer(&xfer);
+}
+
+static inline bool ch34x_control_out(cdch_interface_t* p_cdc, uint8_t request, uint16_t value, uint16_t index,
+                                     tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+  return ch34x_set_request(p_cdc, TUSB_DIR_OUT, request, value, index, NULL, 0, complete_cb, user_data);
+}
+
+static inline bool ch34x_control_in(cdch_interface_t* p_cdc, uint8_t request, uint16_t value, uint16_t index,
+                                    uint8_t* buffer, uint16_t buffersize, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+  return ch34x_set_request(p_cdc, TUSB_DIR_IN, request, value, index, buffer, buffersize,
+                           complete_cb, user_data);
+}
+
+static bool ch34x_write_reg(cdch_interface_t* p_cdc, uint16_t reg, uint16_t reg_value, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+  return ch34x_control_out(p_cdc, CH34X_REQ_WRITE_REG, reg, reg_value, complete_cb, user_data);
+}
+
+//static bool ch34x_read_reg_request ( cdch_interface_t* p_cdc, uint16_t reg,
+//                                     uint8_t *buffer, uint16_t buffersize, tuh_xfer_cb_t complete_cb, uintptr_t user_data )
+//{
+//  return ch34x_control_in ( p_cdc, CH34X_REQ_READ_REG, reg, 0, buffer, buffersize, complete_cb, user_data );
+//}
+
+static bool ch34x_write_reg_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate,
+                                     tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+  uint16_t const div_ps = ch34x_get_divisor_prescaler(baudrate);
+  TU_VERIFY(div_ps != 0);
+  TU_ASSERT(ch34x_write_reg(p_cdc, CH34X_REG16_DIVISOR_PRESCALER, div_ps,
+                            complete_cb, user_data));
+  return true;
+}
+
+//------------- Driver API -------------//
+
+// internal control complete to update state such as line state, encoding
+static void ch34x_control_complete(tuh_xfer_t* xfer) {
+  // CH34x only has 1 interface and use wIndex as payload and not for bInterfaceNumber
+  process_internal_control_complete(xfer, 0);
+}
+
+static bool ch34x_set_data_format(cdch_interface_t* p_cdc, uint8_t stop_bits, uint8_t parity, uint8_t data_bits,
+                                tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+  p_cdc->requested_line_coding.stop_bits = stop_bits;
+  p_cdc->requested_line_coding.parity = parity;
+  p_cdc->requested_line_coding.data_bits = data_bits;
+
+  uint8_t const lcr = ch34x_get_lcr(stop_bits, parity, data_bits);
+  TU_VERIFY(lcr != 0);
+  TU_ASSERT (ch34x_control_out(p_cdc, CH34X_REQ_WRITE_REG, CH32X_REG16_LCR2_LCR, lcr,
+                               complete_cb ? ch34x_control_complete : NULL, user_data));
+  return true;
+}
+
+static bool ch34x_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate,
+                               tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+  p_cdc->requested_line_coding.bit_rate = baudrate;
+  p_cdc->user_control_cb = complete_cb;
+  TU_ASSERT(ch34x_write_reg_baudrate(p_cdc, baudrate,
+                                     complete_cb ? ch34x_control_complete : NULL, user_data));
+  return true;
+}
+
+static void ch34x_set_line_coding_stage1_complete(tuh_xfer_t* xfer) {
+  uint8_t const itf_num = 0;
+  uint8_t const idx = tuh_cdc_itf_get_index(xfer->daddr, itf_num);
+  cdch_interface_t* p_cdc = get_itf(idx);
+  TU_ASSERT(p_cdc, );
+
+  if (xfer->result == XFER_RESULT_SUCCESS) {
+    // stage 1 success, continue to stage 2
+    p_cdc->line_coding.bit_rate = p_cdc->requested_line_coding.bit_rate;
+    TU_ASSERT(ch34x_set_data_format(p_cdc, p_cdc->requested_line_coding.stop_bits, p_cdc->requested_line_coding.parity,
+                                    p_cdc->requested_line_coding.data_bits, ch34x_control_complete, xfer->user_data), );
+  } else {
+    // stage 1 failed, notify user
+    xfer->complete_cb = p_cdc->user_control_cb;
+    if (xfer->complete_cb) {
+      xfer->complete_cb(xfer);
+    }
+  }
+}
+
+// 2 stages: set baudrate (stage1) + set data format (stage2)
+static bool ch34x_set_line_coding(cdch_interface_t* p_cdc, cdc_line_coding_t const* line_coding,
+                                  tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+  p_cdc->requested_line_coding = *line_coding;
+  p_cdc->user_control_cb = complete_cb;
+
+  if (complete_cb) {
+    // stage 1 set baudrate
+    TU_ASSERT(ch34x_write_reg_baudrate(p_cdc, line_coding->bit_rate,
+                                       ch34x_set_line_coding_stage1_complete, user_data));
+  } else {
+    // sync call
+    xfer_result_t result;
+
+    // stage 1 set baudrate
+    TU_ASSERT(ch34x_write_reg_baudrate(p_cdc, line_coding->bit_rate, NULL, (uintptr_t) &result));
+    TU_VERIFY(result == XFER_RESULT_SUCCESS);
+    p_cdc->line_coding.bit_rate = line_coding->bit_rate;
+
+    // stage 2 set data format
+    TU_ASSERT(ch34x_set_data_format(p_cdc, line_coding->stop_bits, line_coding->parity, line_coding->data_bits,
+                                    NULL, (uintptr_t) &result));
+    TU_VERIFY(result == XFER_RESULT_SUCCESS);
+    p_cdc->line_coding.stop_bits = line_coding->stop_bits;
+    p_cdc->line_coding.parity = line_coding->parity;
+    p_cdc->line_coding.data_bits = line_coding->data_bits;
+
+    // update transfer result, user_data is expected to point to xfer_result_t
+    if (user_data) {
+      user_data = result;
+    }
+  }
+
+  return true;
+}
+
+static bool ch34x_set_modem_ctrl(cdch_interface_t* p_cdc, uint16_t line_state,
+                                 tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+  uint8_t control = 0;
+  if (line_state & CDC_CONTROL_LINE_STATE_RTS) {
+    control |= CH34X_BIT_RTS;
+  }
+  if (line_state & CDC_CONTROL_LINE_STATE_DTR) {
+    control |= CH34X_BIT_DTR;
+  }
+
+  // CH34x signals are inverted
+  control = ~control;
+
+  p_cdc->user_control_cb = complete_cb;
+  TU_ASSERT (ch34x_control_out(p_cdc, CH34X_REQ_MODEM_CTRL, control, 0,
+                               complete_cb ? ch34x_control_complete : NULL, user_data));
+  return true;
+}
+
+//------------- Enumeration -------------//
+enum {
+  CONFIG_CH34X_READ_VERSION = 0,
+  CONFIG_CH34X_SERIAL_INIT,
+  CONFIG_CH34X_SPECIAL_REG_WRITE,
+  CONFIG_CH34X_FLOW_CONTROL,
+  CONFIG_CH34X_MODEM_CONTROL,
+  CONFIG_CH34X_COMPLETE
+};
+
+static bool ch34x_open(uint8_t daddr, tusb_desc_interface_t const* itf_desc, uint16_t max_len) {
+  // CH34x Interface includes 1 vendor interface + 2 bulk + 1 interrupt endpoints
+  TU_VERIFY (itf_desc->bNumEndpoints == 3);
+  TU_VERIFY (sizeof(tusb_desc_interface_t) + 3 * sizeof(tusb_desc_endpoint_t) <= max_len);
+
+  cdch_interface_t* p_cdc = make_new_itf(daddr, itf_desc);
+  TU_VERIFY (p_cdc);
+
+  TU_LOG_DRV ("CH34x opened\r\n");
+  p_cdc->serial_drid = SERIAL_DRIVER_CH34X;
+
+  tusb_desc_endpoint_t const* desc_ep = (tusb_desc_endpoint_t const*) tu_desc_next(itf_desc);
+
+  // data endpoints expected to be in pairs
+  TU_ASSERT(open_ep_stream_pair(p_cdc, desc_ep));
+  desc_ep += 2;
+
+  // Interrupt endpoint: not used for now
+  TU_ASSERT(TUSB_DESC_ENDPOINT == tu_desc_type(desc_ep) &&
+            TUSB_XFER_INTERRUPT == desc_ep->bmAttributes.xfer);
+  TU_ASSERT(tuh_edpt_open(daddr, desc_ep));
+  p_cdc->ep_notif = desc_ep->bEndpointAddress;
+
+  return true;
+}
+
+static void ch34x_process_config(tuh_xfer_t* xfer) {
+  // CH34x only has 1 interface and use wIndex as payload and not for bInterfaceNumber
+  uint8_t const itf_num = 0;
+  uint8_t const idx = tuh_cdc_itf_get_index(xfer->daddr, itf_num);
+  cdch_interface_t* p_cdc = get_itf(idx);
+  uintptr_t const state = xfer->user_data;
+  uint8_t buffer[2]; // TODO remove
+  TU_ASSERT (p_cdc,);
+
+  // TODO check xfer->result
+
+  switch (state) {
+    case CONFIG_CH34X_READ_VERSION:
+      TU_LOG_DRV("[%u] CDCh CH34x attempt to read Chip Version\r\n", p_cdc->daddr);
+      TU_ASSERT (ch34x_control_in(p_cdc, CH34X_REQ_READ_VERSION, 0, 0, buffer, 2, ch34x_process_config, CONFIG_CH34X_SERIAL_INIT),);
+      break;
+
+    case CONFIG_CH34X_SERIAL_INIT: {
+      // handle version read data, set CH34x line coding (incl. baudrate)
+      uint8_t const version = xfer->buffer[0];
+      TU_LOG_DRV("[%u] CDCh CH34x Chip Version = %02x\r\n", p_cdc->daddr, version);
+      // only versions >= 0x30 are tested, below 0x30 seems having other programming, see drivers from WCH vendor, Linux kernel and FreeBSD
+      TU_ASSERT (version >= 0x30,);
+      // init CH34x with line coding
+      cdc_line_coding_t const line_coding = CFG_TUH_CDC_LINE_CODING_ON_ENUM_CH34X;
+      uint16_t const div_ps = ch34x_get_divisor_prescaler(line_coding.bit_rate);
+      TU_ASSERT(div_ps != 0, );
+      uint8_t const lcr = ch34x_get_lcr(line_coding.stop_bits, line_coding.parity, line_coding.data_bits);
+      TU_ASSERT(lcr != 0, );
+      TU_ASSERT (ch34x_control_out(p_cdc, CH34X_REQ_SERIAL_INIT, tu_u16(lcr, 0x9c), div_ps,
+                                   ch34x_process_config, CONFIG_CH34X_SPECIAL_REG_WRITE),);
+      break;
+    }
+
+    case CONFIG_CH34X_SPECIAL_REG_WRITE:
+      // overtake line coding and do special reg write, purpose unknown, overtaken from WCH driver
+      p_cdc->line_coding = ((cdc_line_coding_t) CFG_TUH_CDC_LINE_CODING_ON_ENUM_CH34X);
+      TU_ASSERT (ch34x_write_reg(p_cdc, TU_U16(CH341_REG_0x0F, CH341_REG_0x2C), 0x0007, ch34x_process_config, CONFIG_CH34X_FLOW_CONTROL),);
+      break;
+
+    case CONFIG_CH34X_FLOW_CONTROL:
+      // no hardware flow control
+      TU_ASSERT (ch34x_write_reg(p_cdc, TU_U16(CH341_REG_0x27, CH341_REG_0x27), 0x0000, ch34x_process_config, CONFIG_CH34X_MODEM_CONTROL),);
+      break;
+
+    case CONFIG_CH34X_MODEM_CONTROL:
+      // !always! set modem controls RTS/DTR (CH34x has no reset state after CH34X_REQ_SERIAL_INIT)
+      TU_ASSERT (ch34x_set_modem_ctrl(p_cdc, CFG_TUH_CDC_LINE_CONTROL_ON_ENUM, ch34x_process_config, CONFIG_CH34X_COMPLETE),);
+      break;
+
+    case CONFIG_CH34X_COMPLETE:
+      set_config_complete(p_cdc, idx, itf_num);
+      break;
+
+    default:
+      TU_ASSERT (false,);
+      break;
+  }
+}
+
+//------------- CH34x helper  -------------//
+
+// calculate divisor and prescaler for baudrate, return it as 16-bit combined value
+static uint16_t ch34x_get_divisor_prescaler(uint32_t baval) {
+  uint8_t a;
+  uint8_t b;
+  uint32_t c;
+
+  TU_VERIFY(baval != 0, 0);
+  switch (baval) {
+    case 921600:
+      a = 0xf3;
+      b = 7;
+      break;
+
+    case 307200:
+      a = 0xd9;
+      b = 7;
+      break;
+
+    default:
+      if (baval > 6000000 / 255) {
+        b = 3;
+        c = 6000000;
+      } else if (baval > 750000 / 255) {
+        b = 2;
+        c = 750000;
+      } else if (baval > 93750 / 255) {
+        b = 1;
+        c = 93750;
+      } else {
+        b = 0;
+        c = 11719;
+      }
+      a = (uint8_t) (c / baval);
+      if (a == 0 || a == 0xFF) {
+        return 0;
+      }
+      if ((c / a - baval) > (baval - c / (a + 1))) {
+        a++;
+      }
+      a = (uint8_t) (256 - a);
+      break;
+  }
+
+  // reg divisor = a, reg prescaler = b
+  // According to linux code we need to set bit 7 of UCHCOM_REG_BPS_PRE,
+  // otherwise the chip will buffer data.
+  return (uint16_t) ((uint16_t)a << 8 | 0x80 | b);
+}
+
+// calculate lcr value from data coding
+static uint8_t ch34x_get_lcr(uint8_t stop_bits, uint8_t parity, uint8_t data_bits) {
+  uint8_t lcr = CH34X_LCR_ENABLE_RX | CH34X_LCR_ENABLE_TX;
+  TU_VERIFY(data_bits >= 5 && data_bits <= 8, 0);
+  lcr |= (uint8_t) (data_bits - 5);
+
+  switch(parity) {
+    case CDC_LINE_CODING_PARITY_NONE:
+      break;
+
+    case CDC_LINE_CODING_PARITY_ODD:
+    lcr |= CH34X_LCR_ENABLE_PAR;
+      break;
+
+    case CDC_LINE_CODING_PARITY_EVEN:
+      lcr |= CH34X_LCR_ENABLE_PAR | CH34X_LCR_PAR_EVEN;
+      break;
+
+    case CDC_LINE_CODING_PARITY_MARK:
+      lcr |= CH34X_LCR_ENABLE_PAR | CH34X_LCR_MARK_SPACE;
+      break;
+
+    case CDC_LINE_CODING_PARITY_SPACE:
+      lcr |= CH34X_LCR_ENABLE_PAR | CH34X_LCR_MARK_SPACE | CH34X_LCR_PAR_EVEN;
+      break;
+
+    default: break;
+  }
+
+  // 1.5 stop bits not supported
+  TU_VERIFY(stop_bits != CDC_LINE_CODING_STOP_BITS_1_5, 0);
+  if (stop_bits == CDC_LINE_CODING_STOP_BITS_2) {
+    lcr |= CH34X_LCR_STOP_BITS_2;
+  }
+
+  return lcr;
+}
+
+
+#endif // CFG_TUH_CDC_CH34X
+
 #endif
diff --git a/src/class/cdc/cdc_host.h b/src/class/cdc/cdc_host.h
index 9e5edd94e..d512a23a5 100644
--- a/src/class/cdc/cdc_host.h
+++ b/src/class/cdc/cdc_host.h
@@ -148,8 +148,11 @@ bool tuh_cdc_set_control_line_state(uint8_t idx, uint16_t line_state, tuh_xfer_c
 // Request to set baudrate
 bool tuh_cdc_set_baudrate(uint8_t idx, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
 
-// Request to Set Line Coding (ACM only)
-// Should only use if you don't work with serial devices such as FTDI/CP210x
+// Request to set data format
+bool tuh_cdc_set_data_format(uint8_t idx, uint8_t stop_bits, uint8_t parity, uint8_t data_bits, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+
+// Request to Set Line Coding = baudrate + data format
+// Note: only implemented by ACM and CH34x, not supported by FTDI and CP210x yet
 bool tuh_cdc_set_line_coding(uint8_t idx, cdc_line_coding_t const* line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
 
 // Request to Get Line Coding (ACM only)
@@ -159,15 +162,13 @@ bool tuh_cdc_set_line_coding(uint8_t idx, cdc_line_coding_t const* line_coding,
 
 // Connect by set both DTR, RTS
 TU_ATTR_ALWAYS_INLINE static inline
-bool tuh_cdc_connect(uint8_t idx, tuh_xfer_cb_t complete_cb, uintptr_t user_data)
-{
+bool tuh_cdc_connect(uint8_t idx, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
   return tuh_cdc_set_control_line_state(idx, CDC_CONTROL_LINE_STATE_DTR | CDC_CONTROL_LINE_STATE_RTS, complete_cb, user_data);
 }
 
 // Disconnect by clear both DTR, RTS
 TU_ATTR_ALWAYS_INLINE static inline
-bool tuh_cdc_disconnect(uint8_t idx, tuh_xfer_cb_t complete_cb, uintptr_t user_data)
-{
+bool tuh_cdc_disconnect(uint8_t idx, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
   return tuh_cdc_set_control_line_state(idx, 0x00, complete_cb, user_data);
 }
 
diff --git a/src/class/cdc/serial/ch34x.h b/src/class/cdc/serial/ch34x.h
new file mode 100644
index 000000000..c18066f57
--- /dev/null
+++ b/src/class/cdc/serial/ch34x.h
@@ -0,0 +1,84 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2023 Heiko Kuester
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#ifndef _CH34X_H_
+#define _CH34X_H_
+
+// There is no official documentation for the CH34x (CH340, CH341) chips. Reference can be found
+// - https://github.com/WCHSoftGroup/ch341ser_linux
+// - https://github.com/torvalds/linux/blob/master/drivers/usb/serial/ch341.c
+// - https://github.com/freebsd/freebsd-src/blob/main/sys/dev/usb/serial/uchcom.c
+
+// set line_coding @ enumeration
+#ifdef CFG_TUH_CDC_LINE_CODING_ON_ENUM
+#define CFG_TUH_CDC_LINE_CODING_ON_ENUM_CH34X CFG_TUH_CDC_LINE_CODING_ON_ENUM
+#else // this default is necessary to work properly
+#define CFG_TUH_CDC_LINE_CODING_ON_ENUM_CH34X { 9600, CDC_LINE_CONDING_STOP_BITS_1, CDC_LINE_CODING_PARITY_NONE, 8 }
+#endif
+
+// USB requests
+#define CH34X_REQ_READ_VERSION 0x5F // dec  95
+#define CH34X_REQ_WRITE_REG    0x9A // dec 154
+#define CH34X_REQ_READ_REG     0x95 // dec 149
+#define CH34X_REQ_SERIAL_INIT  0xA1 // dec 161
+#define CH34X_REQ_MODEM_CTRL   0xA4 // dev 164
+
+// registers
+#define CH34X_REG_BREAK        0x05
+#define CH34X_REG_PRESCALER    0x12
+#define CH34X_REG_DIVISOR      0x13
+#define CH34X_REG_LCR          0x18
+#define CH34X_REG_LCR2         0x25
+#define CH34X_REG_MCR_MSR      0x06
+#define CH34X_REG_MCR_MSR2     0x07
+#define CH34X_NBREAK_BITS      0x01
+
+#define CH341_REG_0x0F         0x0F // undocumented register
+#define CH341_REG_0x2C         0x2C // undocumented register
+#define CH341_REG_0x27         0x27 // hardware flow control (cts/rts)
+
+#define CH34X_REG16_DIVISOR_PRESCALER  TU_U16(CH34X_REG_DIVISOR, CH34X_REG_PRESCALER)
+#define CH32X_REG16_LCR2_LCR           TU_U16(CH34X_REG_LCR2, CH34X_REG_LCR)
+
+// modem control bits
+#define CH34X_BIT_RTS ( 1 << 6 )
+#define CH34X_BIT_DTR ( 1 << 5 )
+
+// line control bits
+#define CH34X_LCR_ENABLE_RX    0x80
+#define CH34X_LCR_ENABLE_TX    0x40
+#define CH34X_LCR_MARK_SPACE   0x20
+#define CH34X_LCR_PAR_EVEN     0x10
+#define CH34X_LCR_ENABLE_PAR   0x08
+#define CH34X_LCR_PAR_MASK     0x38 // all parity bits
+#define CH34X_LCR_STOP_BITS_2  0x04
+#define CH34X_LCR_CS8          0x03
+#define CH34X_LCR_CS7          0x02
+#define CH34X_LCR_CS6          0x01
+#define CH34X_LCR_CS5          0x00
+#define CH34X_LCR_CS_MASK      0x03 // all CSx bits
+
+#endif /* _CH34X_H_ */
diff --git a/src/class/video/video_device.c b/src/class/video/video_device.c
index 3b29454a3..c16a45fcd 100644
--- a/src/class/video/video_device.c
+++ b/src/class/video/video_device.c
@@ -434,8 +434,9 @@ static bool _update_streaming_parameters(videod_streaming_interface_t const *stm
   uint_fast32_t interval_ms = interval / 10000;
   TU_ASSERT(interval_ms);
   uint_fast32_t payload_size = (frame_size + interval_ms - 1) / interval_ms + 2;
-  if (CFG_TUD_VIDEO_STREAMING_EP_BUFSIZE < payload_size)
+  if (CFG_TUD_VIDEO_STREAMING_EP_BUFSIZE < payload_size) {
     payload_size = CFG_TUD_VIDEO_STREAMING_EP_BUFSIZE;
+  }
   param->dwMaxPayloadTransferSize = payload_size;
   return true;
 }
@@ -577,8 +578,9 @@ static bool _negotiate_streaming_parameters(videod_streaming_interface_t const *
       } else {
         payload_size = (frame_size + interval_ms - 1) / interval_ms + 2;
       }
-      if (CFG_TUD_VIDEO_STREAMING_EP_BUFSIZE < payload_size)
+      if (CFG_TUD_VIDEO_STREAMING_EP_BUFSIZE < payload_size) {
         payload_size = CFG_TUD_VIDEO_STREAMING_EP_BUFSIZE;
+      }
       param->dwMaxPayloadTransferSize = payload_size;
     }
     return true;
diff --git a/src/common/tusb_common.h b/src/common/tusb_common.h
index caeb5f2ef..1f08ce4ed 100644
--- a/src/common/tusb_common.h
+++ b/src/common/tusb_common.h
@@ -37,6 +37,7 @@
 #define TU_ARRAY_SIZE(_arr)   ( sizeof(_arr) / sizeof(_arr[0]) )
 #define TU_MIN(_x, _y)        ( ( (_x) < (_y) ) ? (_x) : (_y) )
 #define TU_MAX(_x, _y)        ( ( (_x) > (_y) ) ? (_x) : (_y) )
+#define TU_DIV_CEIL(n, d)     (((n) + (d) - 1) / (d))
 
 #define TU_U16(_high, _low)   ((uint16_t) (((_high) << 8) | (_low)))
 #define TU_U16_HIGH(_u16)     ((uint8_t) (((_u16) >> 8) & 0x00ff))
diff --git a/src/common/tusb_types.h b/src/common/tusb_types.h
index fab680989..d4ef1fd09 100644
--- a/src/common/tusb_types.h
+++ b/src/common/tusb_types.h
@@ -44,43 +44,38 @@
  *------------------------------------------------------------------*/
 
 /// defined base on EHCI specs value for Endpoint Speed
-typedef enum
-{
+typedef enum {
   TUSB_SPEED_FULL = 0,
   TUSB_SPEED_LOW  = 1,
   TUSB_SPEED_HIGH = 2,
   TUSB_SPEED_INVALID = 0xff,
-}tusb_speed_t;
+} tusb_speed_t;
 
 /// defined base on USB Specs Endpoint's bmAttributes
-typedef enum
-{
+typedef enum {
   TUSB_XFER_CONTROL = 0 ,
   TUSB_XFER_ISOCHRONOUS ,
   TUSB_XFER_BULK        ,
   TUSB_XFER_INTERRUPT
-}tusb_xfer_type_t;
+} tusb_xfer_type_t;
 
-typedef enum
-{
+typedef enum {
   TUSB_DIR_OUT = 0,
   TUSB_DIR_IN  = 1,
 
   TUSB_DIR_IN_MASK = 0x80
-}tusb_dir_t;
+} tusb_dir_t;
 
-enum
-{
+enum {
   TUSB_EPSIZE_BULK_FS = 64,
-  TUSB_EPSIZE_BULK_HS= 512,
+  TUSB_EPSIZE_BULK_HS = 512,
 
   TUSB_EPSIZE_ISO_FS_MAX = 1023,
   TUSB_EPSIZE_ISO_HS_MAX = 1024,
 };
 
-/// Isochronous End Point Attributes
-typedef enum
-{
+/// Isochronous Endpoint Attributes
+typedef enum {
   TUSB_ISO_EP_ATT_NO_SYNC         = 0x00,
   TUSB_ISO_EP_ATT_ASYNCHRONOUS    = 0x04,
   TUSB_ISO_EP_ATT_ADAPTIVE        = 0x08,
@@ -88,11 +83,10 @@ typedef enum
   TUSB_ISO_EP_ATT_DATA            = 0x00, ///< Data End Point
   TUSB_ISO_EP_ATT_EXPLICIT_FB     = 0x10, ///< Feedback End Point
   TUSB_ISO_EP_ATT_IMPLICIT_FB     = 0x20, ///< Data endpoint that also serves as an implicit feedback
-}tusb_iso_ep_attribute_t;
+} tusb_iso_ep_attribute_t;
 
 /// USB Descriptor Types
-typedef enum
-{
+typedef enum {
   TUSB_DESC_DEVICE                = 0x01,
   TUSB_DESC_CONFIGURATION         = 0x02,
   TUSB_DESC_STRING                = 0x03,
@@ -119,10 +113,9 @@ typedef enum
 
   TUSB_DESC_SUPERSPEED_ENDPOINT_COMPANION     = 0x30,
   TUSB_DESC_SUPERSPEED_ISO_ENDPOINT_COMPANION = 0x31
-}tusb_desc_type_t;
+} tusb_desc_type_t;
 
-typedef enum
-{
+typedef enum {
   TUSB_REQ_GET_STATUS        = 0  ,
   TUSB_REQ_CLEAR_FEATURE     = 1  ,
   TUSB_REQ_RESERVED          = 2  ,
@@ -136,25 +129,22 @@ typedef enum
   TUSB_REQ_GET_INTERFACE     = 10 ,
   TUSB_REQ_SET_INTERFACE     = 11 ,
   TUSB_REQ_SYNCH_FRAME       = 12
-}tusb_request_code_t;
+} tusb_request_code_t;
 
-typedef enum
-{
+typedef enum {
   TUSB_REQ_FEATURE_EDPT_HALT     = 0,
   TUSB_REQ_FEATURE_REMOTE_WAKEUP = 1,
   TUSB_REQ_FEATURE_TEST_MODE     = 2
-}tusb_request_feature_selector_t;
+} tusb_request_feature_selector_t;
 
-typedef enum
-{
+typedef enum {
   TUSB_REQ_TYPE_STANDARD = 0,
   TUSB_REQ_TYPE_CLASS,
   TUSB_REQ_TYPE_VENDOR,
   TUSB_REQ_TYPE_INVALID
 } tusb_request_type_t;
 
-typedef enum
-{
+typedef enum {
   TUSB_REQ_RCPT_DEVICE =0,
   TUSB_REQ_RCPT_INTERFACE,
   TUSB_REQ_RCPT_ENDPOINT,
@@ -162,8 +152,7 @@ typedef enum
 } tusb_request_recipient_t;
 
 // https://www.usb.org/defined-class-codes
-typedef enum
-{
+typedef enum {
   TUSB_CLASS_UNSPECIFIED          = 0    ,
   TUSB_CLASS_AUDIO                = 1    ,
   TUSB_CLASS_CDC                  = 2    ,
@@ -187,26 +176,23 @@ typedef enum
   TUSB_CLASS_MISC                 = 0xEF ,
   TUSB_CLASS_APPLICATION_SPECIFIC = 0xFE ,
   TUSB_CLASS_VENDOR_SPECIFIC      = 0xFF
-}tusb_class_code_t;
+} tusb_class_code_t;
 
 typedef enum
 {
   MISC_SUBCLASS_COMMON = 2
 }misc_subclass_type_t;
 
-typedef enum
-{
+typedef enum {
   MISC_PROTOCOL_IAD = 1
-}misc_protocol_type_t;
+} misc_protocol_type_t;
 
-typedef enum
-{
+typedef enum {
   APP_SUBCLASS_USBTMC = 0x03,
   APP_SUBCLASS_DFU_RUNTIME = 0x01
 } app_subclass_type_t;
 
-typedef enum
-{
+typedef enum {
   DEVICE_CAPABILITY_WIRELESS_USB               = 0x01,
   DEVICE_CAPABILITY_USB20_EXTENSION            = 0x02,
   DEVICE_CAPABILITY_SUPERSPEED_USB             = 0x03,
@@ -223,7 +209,7 @@ typedef enum
   DEVICE_CAPABILITY_AUTHENTICATION             = 0x0E,
   DEVICE_CAPABILITY_BILLBOARD_EX               = 0x0F,
   DEVICE_CAPABILITY_CONFIGURATION_SUMMARY      = 0x10
-}device_capability_type_t;
+} device_capability_type_t;
 
 enum {
   TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP = TU_BIT(5),
@@ -235,28 +221,25 @@ enum {
 //--------------------------------------------------------------------+
 //
 //--------------------------------------------------------------------+
-typedef enum
-{
+typedef enum {
   XFER_RESULT_SUCCESS = 0,
   XFER_RESULT_FAILED,
   XFER_RESULT_STALLED,
   XFER_RESULT_TIMEOUT,
   XFER_RESULT_INVALID
-}xfer_result_t;
+} xfer_result_t;
 
-enum // TODO remove
-{
+// TODO remove
+enum {
   DESC_OFFSET_LEN  = 0,
   DESC_OFFSET_TYPE = 1
 };
 
-enum
-{
+enum {
   INTERFACE_INVALID_NUMBER = 0xff
 };
 
-typedef enum
-{
+typedef enum {
   MS_OS_20_SET_HEADER_DESCRIPTOR       = 0x00,
   MS_OS_20_SUBSET_HEADER_CONFIGURATION = 0x01,
   MS_OS_20_SUBSET_HEADER_FUNCTION      = 0x02,
@@ -268,16 +251,14 @@ typedef enum
   MS_OS_20_FEATURE_VENDOR_REVISION     = 0x08
 } microsoft_os_20_type_t;
 
-enum
-{
+enum {
   CONTROL_STAGE_IDLE,
   CONTROL_STAGE_SETUP,
   CONTROL_STAGE_DATA,
   CONTROL_STAGE_ACK
 };
 
-enum
-{
+enum {
   TUSB_INDEX_INVALID_8 = 0xFFu
 };
 
@@ -290,8 +271,7 @@ TU_ATTR_PACKED_BEGIN
 TU_ATTR_BIT_FIELD_ORDER_BEGIN
 
 /// USB Device Descriptor
-typedef struct TU_ATTR_PACKED
-{
+typedef struct TU_ATTR_PACKED {
   uint8_t  bLength            ; ///< Size of this descriptor in bytes.
   uint8_t  bDescriptorType    ; ///< DEVICE Descriptor Type.
   uint16_t bcdUSB             ; ///< BUSB Specification Release Number in Binary-Coded Decimal (i.e., 2.10 is 210H). This field identifies the release of the USB Specification with which the device and its descriptors are compliant.
@@ -314,8 +294,7 @@ typedef struct TU_ATTR_PACKED
 TU_VERIFY_STATIC( sizeof(tusb_desc_device_t) == 18, "size is not correct");
 
 // USB Binary Device Object Store (BOS) Descriptor
-typedef struct TU_ATTR_PACKED
-{
+typedef struct TU_ATTR_PACKED {
   uint8_t  bLength         ; ///< Size of this descriptor in bytes
   uint8_t  bDescriptorType ; ///< CONFIGURATION Descriptor Type
   uint16_t wTotalLength    ; ///< Total length of data returned for this descriptor
@@ -325,8 +304,7 @@ typedef struct TU_ATTR_PACKED
 TU_VERIFY_STATIC( sizeof(tusb_desc_bos_t) == 5, "size is not correct");
 
 /// USB Configuration Descriptor
-typedef struct TU_ATTR_PACKED
-{
+typedef struct TU_ATTR_PACKED {
   uint8_t  bLength             ; ///< Size of this descriptor in bytes
   uint8_t  bDescriptorType     ; ///< CONFIGURATION Descriptor Type
   uint16_t wTotalLength        ; ///< Total length of data returned for this configuration. Includes the combined length of all descriptors (configuration, interface, endpoint, and class- or vendor-specific) returned for this configuration.
@@ -341,8 +319,7 @@ typedef struct TU_ATTR_PACKED
 TU_VERIFY_STATIC( sizeof(tusb_desc_configuration_t) == 9, "size is not correct");
 
 /// USB Interface Descriptor
-typedef struct TU_ATTR_PACKED
-{
+typedef struct TU_ATTR_PACKED {
   uint8_t  bLength            ; ///< Size of this descriptor in bytes
   uint8_t  bDescriptorType    ; ///< INTERFACE Descriptor Type
 
@@ -358,8 +335,7 @@ typedef struct TU_ATTR_PACKED
 TU_VERIFY_STATIC( sizeof(tusb_desc_interface_t) == 9, "size is not correct");
 
 /// USB Endpoint Descriptor
-typedef struct TU_ATTR_PACKED
-{
+typedef struct TU_ATTR_PACKED {
   uint8_t  bLength          ; // Size of this descriptor in bytes
   uint8_t  bDescriptorType  ; // ENDPOINT Descriptor Type
 
@@ -379,8 +355,7 @@ typedef struct TU_ATTR_PACKED
 TU_VERIFY_STATIC( sizeof(tusb_desc_endpoint_t) == 7, "size is not correct");
 
 /// USB Other Speed Configuration Descriptor
-typedef struct TU_ATTR_PACKED
-{
+typedef struct TU_ATTR_PACKED {
   uint8_t  bLength             ; ///< Size of descriptor
   uint8_t  bDescriptorType     ; ///< Other_speed_Configuration Type
   uint16_t wTotalLength        ; ///< Total length of data returned
@@ -393,8 +368,7 @@ typedef struct TU_ATTR_PACKED
 } tusb_desc_other_speed_t;
 
 /// USB Device Qualifier Descriptor
-typedef struct TU_ATTR_PACKED
-{
+typedef struct TU_ATTR_PACKED {
   uint8_t  bLength            ; ///< Size of descriptor
   uint8_t  bDescriptorType    ; ///< Device Qualifier Type
   uint16_t bcdUSB             ; ///< USB specification version number (e.g., 0200H for V2.00)
@@ -411,8 +385,7 @@ typedef struct TU_ATTR_PACKED
 TU_VERIFY_STATIC( sizeof(tusb_desc_device_qualifier_t) == 10, "size is not correct");
 
 /// USB Interface Association Descriptor (IAD ECN)
-typedef struct TU_ATTR_PACKED
-{
+typedef struct TU_ATTR_PACKED {
   uint8_t bLength           ; ///< Size of descriptor
   uint8_t bDescriptorType   ; ///< Other_speed_Configuration Type
 
@@ -427,16 +400,14 @@ typedef struct TU_ATTR_PACKED
 } tusb_desc_interface_assoc_t;
 
 // USB String Descriptor
-typedef struct TU_ATTR_PACKED
-{
+typedef struct TU_ATTR_PACKED {
   uint8_t  bLength         ; ///< Size of this descriptor in bytes
   uint8_t  bDescriptorType ; ///< Descriptor Type
   uint16_t unicode_string[];
 } tusb_desc_string_t;
 
 // USB Binary Device Object Store (BOS)
-typedef struct TU_ATTR_PACKED
-{
+typedef struct TU_ATTR_PACKED {
   uint8_t bLength;
   uint8_t bDescriptorType ;
   uint8_t bDevCapabilityType;
@@ -445,9 +416,8 @@ typedef struct TU_ATTR_PACKED
   uint8_t CapabilityData[];
 } tusb_desc_bos_platform_t;
 
-// USB WebuSB URL Descriptor
-typedef struct TU_ATTR_PACKED
-{
+// USB WebUSB URL Descriptor
+typedef struct TU_ATTR_PACKED {
   uint8_t bLength;
   uint8_t bDescriptorType;
   uint8_t bScheme;
@@ -455,8 +425,7 @@ typedef struct TU_ATTR_PACKED
 } tusb_desc_webusb_url_t;
 
 // DFU Functional Descriptor
-typedef struct TU_ATTR_PACKED
-{
+typedef struct TU_ATTR_PACKED {
   uint8_t  bLength;
   uint8_t  bDescriptorType;
 
@@ -481,7 +450,7 @@ typedef struct TU_ATTR_PACKED
 //
 //--------------------------------------------------------------------+
 
-typedef struct TU_ATTR_PACKED{
+typedef struct TU_ATTR_PACKED {
   union {
     struct TU_ATTR_PACKED {
       uint8_t recipient :  5; ///< Recipient type tusb_request_recipient_t.
@@ -509,36 +478,30 @@ TU_ATTR_BIT_FIELD_ORDER_END
 //--------------------------------------------------------------------+
 
 // Get direction from Endpoint address
-TU_ATTR_ALWAYS_INLINE static inline tusb_dir_t tu_edpt_dir(uint8_t addr)
-{
+TU_ATTR_ALWAYS_INLINE static inline tusb_dir_t tu_edpt_dir(uint8_t addr) {
   return (addr & TUSB_DIR_IN_MASK) ? TUSB_DIR_IN : TUSB_DIR_OUT;
 }
 
 // Get Endpoint number from address
-TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_edpt_number(uint8_t addr)
-{
+TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_edpt_number(uint8_t addr) {
   return (uint8_t)(addr & (~TUSB_DIR_IN_MASK));
 }
 
-TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_edpt_addr(uint8_t num, uint8_t dir)
-{
+TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_edpt_addr(uint8_t num, uint8_t dir) {
   return (uint8_t)(num | (dir ? TUSB_DIR_IN_MASK : 0));
 }
 
-TU_ATTR_ALWAYS_INLINE static inline uint16_t tu_edpt_packet_size(tusb_desc_endpoint_t const* desc_ep)
-{
+TU_ATTR_ALWAYS_INLINE static inline uint16_t tu_edpt_packet_size(tusb_desc_endpoint_t const* desc_ep) {
   return tu_le16toh(desc_ep->wMaxPacketSize) & TU_GENMASK(10, 0);
 }
 
 #if CFG_TUSB_DEBUG
-TU_ATTR_ALWAYS_INLINE static inline const char *tu_edpt_dir_str(tusb_dir_t dir)
-{
+TU_ATTR_ALWAYS_INLINE static inline const char *tu_edpt_dir_str(tusb_dir_t dir) {
   tu_static const char *str[] = {"out", "in"};
   return str[dir];
 }
 
-TU_ATTR_ALWAYS_INLINE static inline const char *tu_edpt_type_str(tusb_xfer_type_t t)
-{
+TU_ATTR_ALWAYS_INLINE static inline const char *tu_edpt_type_str(tusb_xfer_type_t t) {
   tu_static const char *str[] = {"control", "isochronous", "bulk", "interrupt"};
   return str[t];
 }
@@ -549,21 +512,18 @@ TU_ATTR_ALWAYS_INLINE static inline const char *tu_edpt_type_str(tusb_xfer_type_
 //--------------------------------------------------------------------+
 
 // return next descriptor
-TU_ATTR_ALWAYS_INLINE static inline uint8_t const * tu_desc_next(void const* desc)
-{
+TU_ATTR_ALWAYS_INLINE static inline uint8_t const * tu_desc_next(void const* desc) {
   uint8_t const* desc8 = (uint8_t const*) desc;
   return desc8 + desc8[DESC_OFFSET_LEN];
 }
 
 // get descriptor type
-TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_desc_type(void const* desc)
-{
+TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_desc_type(void const* desc) {
   return ((uint8_t const*) desc)[DESC_OFFSET_TYPE];
 }
 
 // get descriptor length
-TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_desc_len(void const* desc)
-{
+TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_desc_len(void const* desc) {
   return ((uint8_t const*) desc)[DESC_OFFSET_LEN];
 }
 
diff --git a/src/device/dcd.h b/src/device/dcd.h
index 8c6813cf7..69c26bcf4 100644
--- a/src/device/dcd.h
+++ b/src/device/dcd.h
@@ -152,7 +152,7 @@ void dcd_sof_enable(uint8_t rhport, bool en);
 
 // Invoked when a control transfer's status stage is complete.
 // May help DCD to prepare for next control transfer, this API is optional.
-void dcd_edpt0_status_complete(uint8_t rhport, tusb_control_request_t const * request) TU_ATTR_WEAK;
+void dcd_edpt0_status_complete(uint8_t rhport, tusb_control_request_t const * request);
 
 // Configure endpoint's registers according to descriptor
 bool dcd_edpt_open            (uint8_t rhport, tusb_desc_endpoint_t const * desc_ep);
diff --git a/src/device/usbd.c b/src/device/usbd.c
index 59466b42e..5c94ebcc5 100644
--- a/src/device/usbd.c
+++ b/src/device/usbd.c
@@ -38,11 +38,19 @@
 //--------------------------------------------------------------------+
 // USBD Configuration
 //--------------------------------------------------------------------+
-
 #ifndef CFG_TUD_TASK_QUEUE_SZ
   #define CFG_TUD_TASK_QUEUE_SZ   16
 #endif
 
+//--------------------------------------------------------------------+
+// Callback weak stubs (called if application does not provide)
+//--------------------------------------------------------------------+
+TU_ATTR_WEAK void tud_event_hook_cb(uint8_t rhport, uint32_t eventid, bool in_isr) {
+  (void)rhport;
+  (void)eventid;
+  (void)in_isr;
+}
+
 //--------------------------------------------------------------------+
 // Device Data
 //--------------------------------------------------------------------+
@@ -50,10 +58,8 @@
 // Invalid driver ID in itf2drv[] ep2drv[][] mapping
 enum { DRVID_INVALID = 0xFFu };
 
-typedef struct
-{
-  struct TU_ATTR_PACKED
-  {
+typedef struct {
+  struct TU_ATTR_PACKED {
     volatile uint8_t connected    : 1;
     volatile uint8_t addressed    : 1;
     volatile uint8_t suspended    : 1;
@@ -85,151 +91,150 @@ tu_static usbd_device_t _usbd_dev;
 #endif
 
 // Built-in class drivers
-tu_static usbd_class_driver_t const _usbd_driver[] =
-{
-  #if CFG_TUD_CDC
-  {
-    DRIVER_NAME("CDC")
-    .init             = cdcd_init,
-    .reset            = cdcd_reset,
-    .open             = cdcd_open,
-    .control_xfer_cb  = cdcd_control_xfer_cb,
-    .xfer_cb          = cdcd_xfer_cb,
-    .sof              = NULL
-  },
-  #endif
+tu_static usbd_class_driver_t const _usbd_driver[] = {
+    #if CFG_TUD_CDC
+    {
+        DRIVER_NAME("CDC")
+        .init             = cdcd_init,
+        .reset            = cdcd_reset,
+        .open             = cdcd_open,
+        .control_xfer_cb  = cdcd_control_xfer_cb,
+        .xfer_cb          = cdcd_xfer_cb,
+        .sof              = NULL
+    },
+    #endif
 
-  #if CFG_TUD_MSC
-  {
-    DRIVER_NAME("MSC")
-    .init             = mscd_init,
-    .reset            = mscd_reset,
-    .open             = mscd_open,
-    .control_xfer_cb  = mscd_control_xfer_cb,
-    .xfer_cb          = mscd_xfer_cb,
-    .sof              = NULL
-  },
-  #endif
+    #if CFG_TUD_MSC
+    {
+        DRIVER_NAME("MSC")
+        .init             = mscd_init,
+        .reset            = mscd_reset,
+        .open             = mscd_open,
+        .control_xfer_cb  = mscd_control_xfer_cb,
+        .xfer_cb          = mscd_xfer_cb,
+        .sof              = NULL
+    },
+    #endif
 
-  #if CFG_TUD_HID
-  {
-    DRIVER_NAME("HID")
-    .init             = hidd_init,
-    .reset            = hidd_reset,
-    .open             = hidd_open,
-    .control_xfer_cb  = hidd_control_xfer_cb,
-    .xfer_cb          = hidd_xfer_cb,
-    .sof              = NULL
-  },
-  #endif
+    #if CFG_TUD_HID
+    {
+      DRIVER_NAME("HID")
+      .init             = hidd_init,
+      .reset            = hidd_reset,
+      .open             = hidd_open,
+      .control_xfer_cb  = hidd_control_xfer_cb,
+      .xfer_cb          = hidd_xfer_cb,
+      .sof              = NULL
+    },
+    #endif
 
-  #if CFG_TUD_AUDIO
-  {
-    DRIVER_NAME("AUDIO")
-    .init             = audiod_init,
-    .reset            = audiod_reset,
-    .open             = audiod_open,
-    .control_xfer_cb  = audiod_control_xfer_cb,
-    .xfer_cb          = audiod_xfer_cb,
-    .sof              = audiod_sof_isr
-  },
-  #endif
+    #if CFG_TUD_AUDIO
+    {
+      DRIVER_NAME("AUDIO")
+      .init             = audiod_init,
+      .reset            = audiod_reset,
+      .open             = audiod_open,
+      .control_xfer_cb  = audiod_control_xfer_cb,
+      .xfer_cb          = audiod_xfer_cb,
+      .sof              = audiod_sof_isr
+    },
+    #endif
 
-  #if CFG_TUD_VIDEO
-  {
-    DRIVER_NAME("VIDEO")
-    .init             = videod_init,
-    .reset            = videod_reset,
-    .open             = videod_open,
-    .control_xfer_cb  = videod_control_xfer_cb,
-    .xfer_cb          = videod_xfer_cb,
-    .sof              = NULL
-  },
-  #endif
+    #if CFG_TUD_VIDEO
+    {
+      DRIVER_NAME("VIDEO")
+      .init             = videod_init,
+      .reset            = videod_reset,
+      .open             = videod_open,
+      .control_xfer_cb  = videod_control_xfer_cb,
+      .xfer_cb          = videod_xfer_cb,
+      .sof              = NULL
+    },
+    #endif
 
-  #if CFG_TUD_MIDI
-  {
-    DRIVER_NAME("MIDI")
-    .init             = midid_init,
-    .open             = midid_open,
-    .reset            = midid_reset,
-    .control_xfer_cb  = midid_control_xfer_cb,
-    .xfer_cb          = midid_xfer_cb,
-    .sof              = NULL
-  },
-  #endif
+    #if CFG_TUD_MIDI
+    {
+      DRIVER_NAME("MIDI")
+      .init             = midid_init,
+      .open             = midid_open,
+      .reset            = midid_reset,
+      .control_xfer_cb  = midid_control_xfer_cb,
+      .xfer_cb          = midid_xfer_cb,
+      .sof              = NULL
+    },
+    #endif
 
-  #if CFG_TUD_VENDOR
-  {
-    DRIVER_NAME("VENDOR")
-    .init             = vendord_init,
-    .reset            = vendord_reset,
-    .open             = vendord_open,
-    .control_xfer_cb  = tud_vendor_control_xfer_cb,
-    .xfer_cb          = vendord_xfer_cb,
-    .sof              = NULL
-  },
-  #endif
+    #if CFG_TUD_VENDOR
+    {
+      DRIVER_NAME("VENDOR")
+      .init             = vendord_init,
+      .reset            = vendord_reset,
+      .open             = vendord_open,
+      .control_xfer_cb  = tud_vendor_control_xfer_cb,
+      .xfer_cb          = vendord_xfer_cb,
+      .sof              = NULL
+    },
+    #endif
 
-  #if CFG_TUD_USBTMC
-  {
-    DRIVER_NAME("TMC")
-    .init             = usbtmcd_init_cb,
-    .reset            = usbtmcd_reset_cb,
-    .open             = usbtmcd_open_cb,
-    .control_xfer_cb  = usbtmcd_control_xfer_cb,
-    .xfer_cb          = usbtmcd_xfer_cb,
-    .sof              = NULL
-  },
-  #endif
+    #if CFG_TUD_USBTMC
+    {
+      DRIVER_NAME("TMC")
+      .init             = usbtmcd_init_cb,
+      .reset            = usbtmcd_reset_cb,
+      .open             = usbtmcd_open_cb,
+      .control_xfer_cb  = usbtmcd_control_xfer_cb,
+      .xfer_cb          = usbtmcd_xfer_cb,
+      .sof              = NULL
+    },
+    #endif
 
-  #if CFG_TUD_DFU_RUNTIME
-  {
-    DRIVER_NAME("DFU-RUNTIME")
-    .init             = dfu_rtd_init,
-    .reset            = dfu_rtd_reset,
-    .open             = dfu_rtd_open,
-    .control_xfer_cb  = dfu_rtd_control_xfer_cb,
-    .xfer_cb          = NULL,
-    .sof              = NULL
-  },
-  #endif
+    #if CFG_TUD_DFU_RUNTIME
+    {
+      DRIVER_NAME("DFU-RUNTIME")
+      .init             = dfu_rtd_init,
+      .reset            = dfu_rtd_reset,
+      .open             = dfu_rtd_open,
+      .control_xfer_cb  = dfu_rtd_control_xfer_cb,
+      .xfer_cb          = NULL,
+      .sof              = NULL
+    },
+    #endif
 
-  #if CFG_TUD_DFU
-  {
-    DRIVER_NAME("DFU")
-    .init             = dfu_moded_init,
-    .reset            = dfu_moded_reset,
-    .open             = dfu_moded_open,
-    .control_xfer_cb  = dfu_moded_control_xfer_cb,
-    .xfer_cb          = NULL,
-    .sof              = NULL
-  },
-  #endif
+    #if CFG_TUD_DFU
+    {
+      DRIVER_NAME("DFU")
+      .init             = dfu_moded_init,
+      .reset            = dfu_moded_reset,
+      .open             = dfu_moded_open,
+      .control_xfer_cb  = dfu_moded_control_xfer_cb,
+      .xfer_cb          = NULL,
+      .sof              = NULL
+    },
+    #endif
 
-  #if CFG_TUD_ECM_RNDIS || CFG_TUD_NCM
-  {
-    DRIVER_NAME("NET")
-    .init             = netd_init,
-    .reset            = netd_reset,
-    .open             = netd_open,
-    .control_xfer_cb  = netd_control_xfer_cb,
-    .xfer_cb          = netd_xfer_cb,
-    .sof                  = NULL,
-  },
-  #endif
+    #if CFG_TUD_ECM_RNDIS || CFG_TUD_NCM
+    {
+      DRIVER_NAME("NET")
+      .init             = netd_init,
+      .reset            = netd_reset,
+      .open             = netd_open,
+      .control_xfer_cb  = netd_control_xfer_cb,
+      .xfer_cb          = netd_xfer_cb,
+      .sof                  = NULL,
+    },
+    #endif
 
-  #if CFG_TUD_BTH
-  {
-    DRIVER_NAME("BTH")
-    .init             = btd_init,
-    .reset            = btd_reset,
-    .open             = btd_open,
-    .control_xfer_cb  = btd_control_xfer_cb,
-    .xfer_cb          = btd_xfer_cb,
-    .sof              = NULL
-  },
-  #endif
+    #if CFG_TUD_BTH
+    {
+      DRIVER_NAME("BTH")
+      .init             = btd_init,
+      .reset            = btd_reset,
+      .open             = btd_open,
+      .control_xfer_cb  = btd_control_xfer_cb,
+      .xfer_cb          = btd_xfer_cb,
+      .sof              = NULL
+    },
+    #endif
 };
 
 enum { BUILTIN_DRIVER_COUNT = TU_ARRAY_SIZE(_usbd_driver) };
@@ -275,7 +280,7 @@ tu_static osal_queue_t _usbd_q;
 
 TU_ATTR_ALWAYS_INLINE static inline bool queue_event(dcd_event_t const * event, bool in_isr) {
   bool ret = osal_queue_send(_usbd_q, event, in_isr);
-  if (tud_event_hook_cb) tud_event_hook_cb(event->rhport, event->event_id, in_isr);
+  tud_event_hook_cb(event->rhport, event->event_id, in_isr);
   return ret;
 }
 
@@ -297,27 +302,23 @@ bool usbd_control_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t event,
 // Debug
 //--------------------------------------------------------------------+
 #if CFG_TUSB_DEBUG >= CFG_TUD_LOG_LEVEL
-tu_static char const* const _usbd_event_str[DCD_EVENT_COUNT] =
-{
-  "Invalid"        ,
-  "Bus Reset"      ,
-  "Unplugged"      ,
-  "SOF"            ,
-  "Suspend"        ,
-  "Resume"         ,
-  "Setup Received" ,
-  "Xfer Complete"  ,
-  "Func Call"
+tu_static char const* const _usbd_event_str[DCD_EVENT_COUNT] = {
+    "Invalid",
+    "Bus Reset",
+    "Unplugged",
+    "SOF",
+    "Suspend",
+    "Resume",
+    "Setup Received",
+    "Xfer Complete",
+    "Func Call"
 };
 
 // for usbd_control to print the name of control complete driver
-void usbd_driver_print_control_complete_name(usbd_control_xfer_cb_t callback)
-{
-  for (uint8_t i = 0; i < TOTAL_DRIVER_COUNT; i++)
-  {
-    usbd_class_driver_t const * driver = get_driver(i);
-    if ( driver && driver->control_xfer_cb == callback )
-    {
+void usbd_driver_print_control_complete_name(usbd_control_xfer_cb_t callback) {
+  for (uint8_t i = 0; i < TOTAL_DRIVER_COUNT; i++) {
+    usbd_class_driver_t const* driver = get_driver(i);
+    if (driver && driver->control_xfer_cb == callback) {
       TU_LOG_USBD("  %s control complete\r\n", driver->name);
       return;
     }
@@ -329,43 +330,36 @@ void usbd_driver_print_control_complete_name(usbd_control_xfer_cb_t callback)
 //--------------------------------------------------------------------+
 // Application API
 //--------------------------------------------------------------------+
-tusb_speed_t tud_speed_get(void)
-{
+tusb_speed_t tud_speed_get(void) {
   return (tusb_speed_t) _usbd_dev.speed;
 }
 
-bool tud_connected(void)
-{
+bool tud_connected(void) {
   return _usbd_dev.connected;
 }
 
-bool tud_mounted(void)
-{
+bool tud_mounted(void) {
   return _usbd_dev.cfg_num ? true : false;
 }
 
-bool tud_suspended(void)
-{
+bool tud_suspended(void) {
   return _usbd_dev.suspended;
 }
 
-bool tud_remote_wakeup(void)
-{
+bool tud_remote_wakeup(void) {
   // only wake up host if this feature is supported and enabled and we are suspended
-  TU_VERIFY (_usbd_dev.suspended && _usbd_dev.remote_wakeup_support && _usbd_dev.remote_wakeup_en );
+  TU_VERIFY (_usbd_dev.suspended && _usbd_dev.remote_wakeup_support && _usbd_dev.remote_wakeup_en);
   dcd_remote_wakeup(_usbd_rhport);
   return true;
 }
 
-bool tud_disconnect(void)
-{
+bool tud_disconnect(void) {
   TU_VERIFY(dcd_disconnect);
   dcd_disconnect(_usbd_rhport);
   return true;
 }
 
-bool tud_connect(void)
-{
+bool tud_connect(void) {
   TU_VERIFY(dcd_connect);
   dcd_connect(_usbd_rhport);
   return true;
diff --git a/src/device/usbd.h b/src/device/usbd.h
index 5456148bf..3ab6c813f 100644
--- a/src/device/usbd.h
+++ b/src/device/usbd.h
@@ -147,7 +147,7 @@ TU_ATTR_WEAK void tud_suspend_cb(bool remote_wakeup_en);
 TU_ATTR_WEAK void tud_resume_cb(void);
 
 // Invoked when there is a new usb event, which need to be processed by tud_task()/tud_task_ext()
-TU_ATTR_WEAK void tud_event_hook_cb(uint8_t rhport, uint32_t eventid, bool in_isr);
+void tud_event_hook_cb(uint8_t rhport, uint32_t eventid, bool in_isr);
 
 // Invoked when received control request with VENDOR TYPE
 TU_ATTR_WEAK bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request);
diff --git a/src/device/usbd_control.c b/src/device/usbd_control.c
index 76d062e40..35cce1f7e 100644
--- a/src/device/usbd_control.c
+++ b/src/device/usbd_control.c
@@ -32,24 +32,32 @@
 #include "tusb.h"
 #include "device/usbd_pvt.h"
 
+//--------------------------------------------------------------------+
+// Callback weak stubs (called if application does not provide)
+//--------------------------------------------------------------------+
+TU_ATTR_WEAK void dcd_edpt0_status_complete(uint8_t rhport, tusb_control_request_t const* request) {
+  (void) rhport;
+  (void) request;
+}
+
+//--------------------------------------------------------------------+
+// MACRO CONSTANT TYPEDEF
+//--------------------------------------------------------------------+
+
 #if CFG_TUSB_DEBUG >= CFG_TUD_LOG_LEVEL
 extern void usbd_driver_print_control_complete_name(usbd_control_xfer_cb_t callback);
 #endif
 
-enum
-{
+enum {
   EDPT_CTRL_OUT = 0x00,
-  EDPT_CTRL_IN  = 0x80
+  EDPT_CTRL_IN = 0x80
 };
 
-typedef struct
-{
+typedef struct {
   tusb_control_request_t request;
-
   uint8_t* buffer;
   uint16_t data_len;
   uint16_t total_xferred;
-
   usbd_control_xfer_cb_t complete_cb;
 } usbd_control_xfer_t;
 
@@ -63,20 +71,18 @@ tu_static uint8_t _usbd_ctrl_buf[CFG_TUD_ENDPOINT0_SIZE];
 //--------------------------------------------------------------------+
 
 // Queue ZLP status transaction
-static inline bool _status_stage_xact(uint8_t rhport, tusb_control_request_t const * request)
-{
+static inline bool _status_stage_xact(uint8_t rhport, tusb_control_request_t const* request) {
   // Opposite to endpoint in Data Phase
   uint8_t const ep_addr = request->bmRequestType_bit.direction ? EDPT_CTRL_OUT : EDPT_CTRL_IN;
   return usbd_edpt_xfer(rhport, ep_addr, NULL, 0);
 }
 
 // Status phase
-bool tud_control_status(uint8_t rhport, tusb_control_request_t const * request)
-{
-  _ctrl_xfer.request       = (*request);
-  _ctrl_xfer.buffer        = NULL;
+bool tud_control_status(uint8_t rhport, tusb_control_request_t const* request) {
+  _ctrl_xfer.request = (*request);
+  _ctrl_xfer.buffer = NULL;
   _ctrl_xfer.total_xferred = 0;
-  _ctrl_xfer.data_len      = 0;
+  _ctrl_xfer.data_len = 0;
 
   return _status_stage_xact(rhport, request);
 }
@@ -84,16 +90,15 @@ bool tud_control_status(uint8_t rhport, tusb_control_request_t const * request)
 // Queue a transaction in Data Stage
 // Each transaction has up to Endpoint0's max packet size.
 // This function can also transfer an zero-length packet
-static bool _data_stage_xact(uint8_t rhport)
-{
-  uint16_t const xact_len = tu_min16(_ctrl_xfer.data_len - _ctrl_xfer.total_xferred, CFG_TUD_ENDPOINT0_SIZE);
+static bool _data_stage_xact(uint8_t rhport) {
+  uint16_t const xact_len = tu_min16(_ctrl_xfer.data_len - _ctrl_xfer.total_xferred,
+                                     CFG_TUD_ENDPOINT0_SIZE);
 
   uint8_t ep_addr = EDPT_CTRL_OUT;
 
-  if ( _ctrl_xfer.request.bmRequestType_bit.direction == TUSB_DIR_IN )
-  {
+  if (_ctrl_xfer.request.bmRequestType_bit.direction == TUSB_DIR_IN) {
     ep_addr = EDPT_CTRL_IN;
-    if ( xact_len ) {
+    if (xact_len) {
       TU_VERIFY(0 == tu_memcpy_s(_usbd_ctrl_buf, CFG_TUD_ENDPOINT0_SIZE, _ctrl_xfer.buffer, xact_len));
     }
   }
@@ -103,29 +108,24 @@ static bool _data_stage_xact(uint8_t rhport)
 
 // Transmit data to/from the control endpoint.
 // If the request's wLength is zero, a status packet is sent instead.
-bool tud_control_xfer(uint8_t rhport, tusb_control_request_t const * request, void* buffer, uint16_t len)
-{
-  _ctrl_xfer.request       = (*request);
-  _ctrl_xfer.buffer        = (uint8_t*) buffer;
+bool tud_control_xfer(uint8_t rhport, tusb_control_request_t const* request, void* buffer, uint16_t len) {
+  _ctrl_xfer.request = (*request);
+  _ctrl_xfer.buffer = (uint8_t*) buffer;
   _ctrl_xfer.total_xferred = 0U;
-  _ctrl_xfer.data_len      = tu_min16(len, request->wLength);
+  _ctrl_xfer.data_len = tu_min16(len, request->wLength);
 
-  if (request->wLength > 0U)
-  {
-    if(_ctrl_xfer.data_len > 0U)
-    {
+  if (request->wLength > 0U) {
+    if (_ctrl_xfer.data_len > 0U) {
       TU_ASSERT(buffer);
     }
 
 //    TU_LOG2("  Control total data length is %u bytes\r\n", _ctrl_xfer.data_len);
 
     // Data stage
-    TU_ASSERT( _data_stage_xact(rhport) );
-  }
-  else
-  {
+    TU_ASSERT(_data_stage_xact(rhport));
+  } else {
     // Status stage
-    TU_ASSERT( _status_stage_xact(rhport, request) );
+    TU_ASSERT(_status_stage_xact(rhport, request));
   }
 
   return true;
@@ -134,49 +134,42 @@ bool tud_control_xfer(uint8_t rhport, tusb_control_request_t const * request, vo
 //--------------------------------------------------------------------+
 // USBD API
 //--------------------------------------------------------------------+
-
 void usbd_control_reset(void);
-void usbd_control_set_request(tusb_control_request_t const *request);
-void usbd_control_set_complete_callback( usbd_control_xfer_cb_t fp );
-bool usbd_control_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes);
+void usbd_control_set_request(tusb_control_request_t const* request);
+void usbd_control_set_complete_callback(usbd_control_xfer_cb_t fp);
+bool usbd_control_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes);
 
-void usbd_control_reset(void)
-{
+void usbd_control_reset(void) {
   tu_varclr(&_ctrl_xfer);
 }
 
 // Set complete callback
-void usbd_control_set_complete_callback( usbd_control_xfer_cb_t fp )
-{
+void usbd_control_set_complete_callback(usbd_control_xfer_cb_t fp) {
   _ctrl_xfer.complete_cb = fp;
 }
 
 // for dcd_set_address where DCD is responsible for status response
-void usbd_control_set_request(tusb_control_request_t const *request)
-{
-  _ctrl_xfer.request       = (*request);
-  _ctrl_xfer.buffer        = NULL;
+void usbd_control_set_request(tusb_control_request_t const* request) {
+  _ctrl_xfer.request = (*request);
+  _ctrl_xfer.buffer = NULL;
   _ctrl_xfer.total_xferred = 0;
-  _ctrl_xfer.data_len      = 0;
+  _ctrl_xfer.data_len = 0;
 }
 
 // callback when a transaction complete on
 // - DATA stage of control endpoint or
 // - Status stage
-bool usbd_control_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)
-{
+bool usbd_control_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) {
   (void) result;
 
   // Endpoint Address is opposite to direction bit, this is Status Stage complete event
-  if ( tu_edpt_dir(ep_addr) != _ctrl_xfer.request.bmRequestType_bit.direction )
-  {
+  if (tu_edpt_dir(ep_addr) != _ctrl_xfer.request.bmRequestType_bit.direction) {
     TU_ASSERT(0 == xferred_bytes);
 
     // invoke optional dcd hook if available
-    if (dcd_edpt0_status_complete) dcd_edpt0_status_complete(rhport, &_ctrl_xfer.request);
+    dcd_edpt0_status_complete(rhport, &_ctrl_xfer.request);
 
-    if (_ctrl_xfer.complete_cb)
-    {
+    if (_ctrl_xfer.complete_cb) {
       // TODO refactor with usbd_driver_print_control_complete_name
       _ctrl_xfer.complete_cb(rhport, CONTROL_STAGE_ACK, &_ctrl_xfer.request);
     }
@@ -184,8 +177,7 @@ bool usbd_control_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t result
     return true;
   }
 
-  if ( _ctrl_xfer.request.bmRequestType_bit.direction == TUSB_DIR_OUT )
-  {
+  if (_ctrl_xfer.request.bmRequestType_bit.direction == TUSB_DIR_OUT) {
     TU_VERIFY(_ctrl_xfer.buffer);
     memcpy(_ctrl_xfer.buffer, _usbd_ctrl_buf, xferred_bytes);
     TU_LOG_MEM(CFG_TUD_LOG_LEVEL, _usbd_ctrl_buf, xferred_bytes, 2);
@@ -196,15 +188,14 @@ bool usbd_control_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t result
 
   // Data Stage is complete when all request's length are transferred or
   // a short packet is sent including zero-length packet.
-  if ( (_ctrl_xfer.request.wLength == _ctrl_xfer.total_xferred) || (xferred_bytes < CFG_TUD_ENDPOINT0_SIZE) )
-  {
+  if ((_ctrl_xfer.request.wLength == _ctrl_xfer.total_xferred) ||
+      (xferred_bytes < CFG_TUD_ENDPOINT0_SIZE)) {
     // DATA stage is complete
     bool is_ok = true;
 
     // invoke complete callback if set
     // callback can still stall control in status phase e.g out data does not make sense
-    if ( _ctrl_xfer.complete_cb )
-    {
+    if (_ctrl_xfer.complete_cb) {
       #if CFG_TUSB_DEBUG >= CFG_TUD_LOG_LEVEL
       usbd_driver_print_control_complete_name(_ctrl_xfer.complete_cb);
       #endif
@@ -212,21 +203,17 @@ bool usbd_control_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t result
       is_ok = _ctrl_xfer.complete_cb(rhport, CONTROL_STAGE_DATA, &_ctrl_xfer.request);
     }
 
-    if ( is_ok )
-    {
+    if (is_ok) {
       // Send status
-      TU_ASSERT( _status_stage_xact(rhport, &_ctrl_xfer.request) );
-    }else
-    {
+      TU_ASSERT(_status_stage_xact(rhport, &_ctrl_xfer.request));
+    } else {
       // Stall both IN and OUT control endpoint
       dcd_edpt_stall(rhport, EDPT_CTRL_OUT);
       dcd_edpt_stall(rhport, EDPT_CTRL_IN);
     }
-  }
-  else
-  {
+  } else {
     // More data to transfer
-    TU_ASSERT( _data_stage_xact(rhport) );
+    TU_ASSERT(_data_stage_xact(rhport));
   }
 
   return true;
diff --git a/src/host/hub.c b/src/host/hub.c
index 32f5e0ac7..3bac18698 100644
--- a/src/host/hub.c
+++ b/src/host/hub.c
@@ -435,9 +435,12 @@ static void hub_port_get_status_complete (tuh_xfer_t* xfer)
     // Other changes are: L1 state
     // TODO clear change
 
-    // prepare for next hub status
-    // TODO continue with status_change, or maybe we can do it again with status
-    hub_edpt_status_xfer(daddr);
+    else
+    {
+      // prepare for next hub status
+      // TODO continue with status_change, or maybe we can do it again with status
+      hub_edpt_status_xfer(daddr);
+    }
   }
 }
 
diff --git a/src/host/usbh.c b/src/host/usbh.c
index 4d30d9f81..dfe6ddb42 100644
--- a/src/host/usbh.c
+++ b/src/host/usbh.c
@@ -36,7 +36,6 @@
 //--------------------------------------------------------------------+
 // USBH Configuration
 //--------------------------------------------------------------------+
-
 #ifndef CFG_TUH_TASK_QUEUE_SZ
   #define CFG_TUH_TASK_QUEUE_SZ   16
 #endif
@@ -45,12 +44,19 @@
   #define CFG_TUH_INTERFACE_MAX   8
 #endif
 
+//--------------------------------------------------------------------+
+// Callback weak stubs (called if application does not provide)
+//--------------------------------------------------------------------+
+TU_ATTR_WEAK void tuh_event_hook_cb(uint8_t rhport, uint32_t eventid, bool in_isr) {
+  (void) rhport;
+  (void) eventid;
+  (void) in_isr;
+}
+
 //--------------------------------------------------------------------+
 // USBH-HCD common data structure
 //--------------------------------------------------------------------+
-
-typedef struct
-{
+typedef struct {
   // port
   uint8_t rhport;
   uint8_t hub_addr;
@@ -112,60 +118,58 @@ typedef struct {
 //--------------------------------------------------------------------+
 // MACRO CONSTANT TYPEDEF
 //--------------------------------------------------------------------+
-
 #if CFG_TUSB_DEBUG >= CFG_TUH_LOG_LEVEL
   #define DRIVER_NAME(_name)    .name = _name,
 #else
   #define DRIVER_NAME(_name)
 #endif
 
-static usbh_class_driver_t const usbh_class_drivers[] =
-{
-  #if CFG_TUH_CDC
+static usbh_class_driver_t const usbh_class_drivers[] = {
+    #if CFG_TUH_CDC
     {
-      DRIVER_NAME("CDC")
-      .init       = cdch_init,
-      .open       = cdch_open,
-      .set_config = cdch_set_config,
-      .xfer_cb    = cdch_xfer_cb,
-      .close      = cdch_close
+        DRIVER_NAME("CDC")
+        .init       = cdch_init,
+        .open       = cdch_open,
+        .set_config = cdch_set_config,
+        .xfer_cb    = cdch_xfer_cb,
+        .close      = cdch_close
     },
-  #endif
+    #endif
 
-  #if CFG_TUH_MSC
+    #if CFG_TUH_MSC
     {
-      DRIVER_NAME("MSC")
-      .init       = msch_init,
-      .open       = msch_open,
-      .set_config = msch_set_config,
-      .xfer_cb    = msch_xfer_cb,
-      .close      = msch_close
+        DRIVER_NAME("MSC")
+        .init       = msch_init,
+        .open       = msch_open,
+        .set_config = msch_set_config,
+        .xfer_cb    = msch_xfer_cb,
+        .close      = msch_close
     },
-  #endif
+    #endif
 
-  #if CFG_TUH_HID
+    #if CFG_TUH_HID
     {
-      DRIVER_NAME("HID")
-      .init       = hidh_init,
-      .open       = hidh_open,
-      .set_config = hidh_set_config,
-      .xfer_cb    = hidh_xfer_cb,
-      .close      = hidh_close
+        DRIVER_NAME("HID")
+        .init       = hidh_init,
+        .open       = hidh_open,
+        .set_config = hidh_set_config,
+        .xfer_cb    = hidh_xfer_cb,
+        .close      = hidh_close
     },
-  #endif
+    #endif
 
-  #if CFG_TUH_HUB
+    #if CFG_TUH_HUB
     {
-      DRIVER_NAME("HUB")
-      .init       = hub_init,
-      .open       = hub_open,
-      .set_config = hub_set_config,
-      .xfer_cb    = hub_xfer_cb,
-      .close      = hub_close
+        DRIVER_NAME("HUB")
+        .init       = hub_init,
+        .open       = hub_open,
+        .set_config = hub_set_config,
+        .xfer_cb    = hub_xfer_cb,
+        .close      = hub_close
     },
-  #endif
+    #endif
 
-  #if CFG_TUH_VENDOR
+    #if CFG_TUH_VENDOR
     {
       DRIVER_NAME("VENDOR")
       .init       = cush_init,
@@ -173,7 +177,7 @@ static usbh_class_driver_t const usbh_class_drivers[] =
       .xfer_cb    = cush_isr,
       .close      = cush_close
     }
-  #endif
+    #endif
 };
 
 enum { BUILTIN_DRIVER_COUNT = TU_ARRAY_SIZE(usbh_class_drivers) };
@@ -233,8 +237,7 @@ static uint8_t _usbh_ctrl_buf[CFG_TUH_ENUMERATION_BUFSIZE];
 // Control transfers: since most controllers do not support multiple control transfers
 // on multiple devices concurrently and control transfers are not used much except for
 // enumeration, we will only execute control transfers one at a time.
-CFG_TUH_MEM_SECTION struct
-{
+CFG_TUH_MEM_SECTION struct {
   CFG_TUH_MEM_ALIGN tusb_control_request_t request;
   uint8_t* buffer;
   tuh_xfer_cb_t complete_cb;
@@ -268,7 +271,7 @@ TU_ATTR_WEAK void osal_task_delay(uint32_t msec) {
 
 TU_ATTR_ALWAYS_INLINE static inline bool queue_event(hcd_event_t const * event, bool in_isr) {
   bool ret = osal_queue_send(_usbh_q, event, in_isr);
-  if (tuh_event_hook_cb) tuh_event_hook_cb(event->rhport, event->event_id, in_isr);
+  tuh_event_hook_cb(event->rhport, event->event_id, in_isr);
   return ret;
 }
 
@@ -367,17 +370,14 @@ bool tuh_init(uint8_t controller_id) {
   tu_memclr(_usbh_devices, sizeof(_usbh_devices));
   tu_memclr(&_ctrl_xfer, sizeof(_ctrl_xfer));
 
-  for(uint8_t i=0; iname);
       driver->init();
     }
@@ -456,7 +456,7 @@ void tuh_task_ext(uint32_t timeout_ms, bool in_isr) {
 
         #if CFG_TUH_HUB
         // TODO remove
-        if ( event.connection.hub_addr != 0) {
+        if ( event.connection.hub_addr != 0 && event.connection.hub_port != 0) {
           // done with hub, waiting for next data on status pipe
           (void) hub_edpt_status_xfer( event.connection.hub_addr );
         }
@@ -545,8 +545,7 @@ void tuh_task_ext(uint32_t timeout_ms, bool in_isr) {
 // Control transfer
 //--------------------------------------------------------------------+
 
-static void _control_blocking_complete_cb(tuh_xfer_t* xfer)
-{
+static void _control_blocking_complete_cb(tuh_xfer_t* xfer) {
   // update result
   *((xfer_result_t*) xfer->user_data) = xfer->result;
 }
@@ -625,21 +624,18 @@ bool tuh_control_xfer (tuh_xfer_t* xfer) {
   return true;
 }
 
-TU_ATTR_ALWAYS_INLINE static inline void _set_control_xfer_stage(uint8_t stage)
-{
+TU_ATTR_ALWAYS_INLINE static inline void _set_control_xfer_stage(uint8_t stage) {
   (void) osal_mutex_lock(_usbh_mutex, OSAL_TIMEOUT_WAIT_FOREVER);
   _ctrl_xfer.stage = stage;
   (void) osal_mutex_unlock(_usbh_mutex);
 }
 
-static void _xfer_complete(uint8_t daddr, xfer_result_t result)
-{
+static void _xfer_complete(uint8_t daddr, xfer_result_t result) {
   TU_LOG_USBH("\r\n");
 
   // duplicate xfer since user can execute control transfer within callback
   tusb_control_request_t const request = _ctrl_xfer.request;
-  tuh_xfer_t xfer_temp =
-  {
+  tuh_xfer_t xfer_temp = {
     .daddr       = daddr,
     .ep_addr     = 0,
     .result      = result,
@@ -652,8 +648,7 @@ static void _xfer_complete(uint8_t daddr, xfer_result_t result)
 
   _set_control_xfer_stage(CONTROL_STAGE_IDLE);
 
-  if (xfer_temp.complete_cb)
-  {
+  if (xfer_temp.complete_cb) {
     xfer_temp.complete_cb(&xfer_temp);
   }
 }
@@ -710,17 +705,16 @@ static bool usbh_control_xfer_cb (uint8_t dev_addr, uint8_t ep_addr, xfer_result
 //
 //--------------------------------------------------------------------+
 
-bool tuh_edpt_xfer(tuh_xfer_t* xfer)
-{
-  uint8_t const daddr   = xfer->daddr;
+bool tuh_edpt_xfer(tuh_xfer_t* xfer) {
+  uint8_t const daddr = xfer->daddr;
   uint8_t const ep_addr = xfer->ep_addr;
 
   TU_VERIFY(daddr && ep_addr);
 
   TU_VERIFY(usbh_edpt_claim(daddr, ep_addr));
 
-  if ( !usbh_edpt_xfer_with_callback(daddr, ep_addr, xfer->buffer, (uint16_t) xfer->buflen, xfer->complete_cb, xfer->user_data) )
-  {
+  if (!usbh_edpt_xfer_with_callback(daddr, ep_addr, xfer->buffer, (uint16_t) xfer->buflen,
+                                    xfer->complete_cb, xfer->user_data)) {
     usbh_edpt_release(daddr, ep_addr);
     return false;
   }
diff --git a/src/host/usbh.h b/src/host/usbh.h
index 7591c7672..9ff118543 100644
--- a/src/host/usbh.h
+++ b/src/host/usbh.h
@@ -94,7 +94,7 @@ TU_ATTR_WEAK void tuh_mount_cb (uint8_t daddr);
 TU_ATTR_WEAK void tuh_umount_cb(uint8_t daddr);
 
 // Invoked when there is a new usb event, which need to be processed by tuh_task()/tuh_task_ext()
-TU_ATTR_WEAK void tuh_event_hook_cb(uint8_t rhport, uint32_t eventid, bool in_isr);
+void tuh_event_hook_cb(uint8_t rhport, uint32_t eventid, bool in_isr);
 
 //--------------------------------------------------------------------+
 // APPLICATION API
diff --git a/src/portable/ohci/ohci.c b/src/portable/ohci/ohci.c
index f978b0965..c59d4755e 100644
--- a/src/portable/ohci/ohci.c
+++ b/src/portable/ohci/ohci.c
@@ -157,6 +157,7 @@ static ohci_ed_t * const p_ed_head[] =
 
 static void ed_list_insert(ohci_ed_t * p_pre, ohci_ed_t * p_ed);
 static void ed_list_remove_by_addr(ohci_ed_t * p_head, uint8_t dev_addr);
+static gtd_extra_data_t *gtd_get_extra_data(ohci_gtd_t const * const gtd);
 
 //--------------------------------------------------------------------+
 // USBH-HCD API
@@ -345,7 +346,7 @@ static void gtd_init(ohci_gtd_t *p_td, uint8_t *data_ptr, uint16_t total_bytes)
   tu_memclr(p_td, sizeof(ohci_gtd_t));
 
   p_td->used = 1;
-  p_td->expected_bytes = total_bytes;
+  gtd_get_extra_data(p_td)->expected_bytes = total_bytes;
 
   p_td->buffer_rounding = 1; // less than queued length is not a error
   p_td->delay_interrupt = OHCI_INT_ON_COMPLETE_NO;
@@ -610,6 +611,15 @@ static inline ohci_ed_t* gtd_get_ed(ohci_gtd_t const * const p_qtd)
   }
 }
 
+static gtd_extra_data_t *gtd_get_extra_data(ohci_gtd_t const * const gtd) {
+  if ( gtd_is_control(gtd) ) {
+    uint8_t idx = ((uintptr_t)gtd - (uintptr_t)&ohci_data.control->gtd) / sizeof(ohci_data.control[0]);
+    return &ohci_data.gtd_extra_control[idx];
+  }else {
+    return &ohci_data.gtd_extra[gtd - ohci_data.gtd_pool];
+  }
+}
+
 static inline uint32_t gtd_xfer_byte_left(uint32_t buffer_end, uint32_t current_buffer)
 {
   // 5.2.9 OHCI sample code
@@ -641,8 +651,7 @@ static void done_queue_isr(uint8_t hostid)
     if ( (qtd->delay_interrupt == OHCI_INT_ON_COMPLETE_YES) || (event != XFER_RESULT_SUCCESS) )
     {
       ohci_ed_t * const ed  = gtd_get_ed(qtd);
-
-      uint32_t const xferred_bytes = qtd->expected_bytes - gtd_xfer_byte_left((uint32_t) qtd->buffer_end, (uint32_t) qtd->current_buffer_pointer);
+      uint32_t const xferred_bytes = gtd_get_extra_data(qtd)->expected_bytes - gtd_xfer_byte_left((uint32_t) qtd->buffer_end, (uint32_t) qtd->current_buffer_pointer);
 
       // NOTE Assuming the current list is BULK and there is no other EDs in the list has queued TDs.
       // When there is a error resulting this ED is halted, and this EP still has other queued TD
@@ -651,7 +660,7 @@ static void done_queue_isr(uint8_t hostid)
       // --> HC will not process Control list (due to service ratio when Bulk list not empty)
       // To walk-around this, the halted ED will have TailP = HeadP (empty list condition), when clearing halt
       // the TailP must be set back to NULL for processing remaining TDs
-      if ((event != XFER_RESULT_SUCCESS))
+      if (event != XFER_RESULT_SUCCESS)
       {
         ed->td_tail &= 0x0Ful;
         ed->td_tail |= tu_align16(ed->td_head.address); // mark halted EP as empty queue
diff --git a/src/portable/ohci/ohci.h b/src/portable/ohci/ohci.h
index 2081ffabb..4feefd771 100644
--- a/src/portable/ohci/ohci.h
+++ b/src/portable/ohci/ohci.h
@@ -45,6 +45,9 @@ enum {
 #define ED_MAX       (CFG_TUH_DEVICE_MAX*CFG_TUH_ENDPOINT_MAX)
 #define GTD_MAX      ED_MAX
 
+// tinyUSB's OHCI implementation caps number of EDs to 8 bits
+TU_VERIFY_STATIC (ED_MAX <= 256, "Reduce CFG_TUH_DEVICE_MAX or CFG_TUH_ENDPOINT_MAX");
+
 //--------------------------------------------------------------------+
 // OHCI Data Structure
 //--------------------------------------------------------------------+
@@ -70,9 +73,8 @@ typedef struct TU_ATTR_ALIGNED(16)
 {
 	// Word 0
 	uint32_t used                    : 1;
-	uint32_t index                   : 4;  // endpoint index the td belongs to, or device address in case of control xfer
-  uint32_t expected_bytes          : 13; // TODO available for hcd
-
+  uint32_t index                   : 8; // endpoint index the gtd belongs to, or device address in case of control xfer
+  uint32_t                         : 9; // can be used
   uint32_t buffer_rounding         : 1;
   uint32_t pid                     : 2;
   uint32_t delay_interrupt         : 3;
@@ -152,9 +154,12 @@ typedef struct TU_ATTR_ALIGNED(32)
 
 TU_VERIFY_STATIC( sizeof(ochi_itd_t) == 32, "size is not correct" );
 
+typedef struct {
+  uint16_t expected_bytes; // up to 8192 bytes so max is 13 bits
+} gtd_extra_data_t;
+
 // structure with member alignment required from large to small
-typedef struct TU_ATTR_ALIGNED(256)
-{
+typedef struct TU_ATTR_ALIGNED(256) {
   ohci_hcca_t hcca;
 
   ohci_ed_t bulk_head_ed; // static bulk head (dummy)
@@ -164,14 +169,17 @@ typedef struct TU_ATTR_ALIGNED(256)
   struct {
     ohci_ed_t ed;
     ohci_gtd_t gtd;
-  }control[CFG_TUH_DEVICE_MAX+CFG_TUH_HUB+1];
+  } control[CFG_TUH_DEVICE_MAX + CFG_TUH_HUB + 1];
 
   //  ochi_itd_t itd[OHCI_MAX_ITD]; // itd requires alignment of 32
   ohci_ed_t ed_pool[ED_MAX];
   ohci_gtd_t gtd_pool[GTD_MAX];
 
-  volatile uint16_t frame_number_hi;
+  // extra data needed by TDs that can't fit in the TD struct
+  gtd_extra_data_t gtd_extra_control[CFG_TUH_DEVICE_MAX + CFG_TUH_HUB + 1];
+  gtd_extra_data_t gtd_extra[GTD_MAX];
 
+  volatile uint16_t frame_number_hi;
 } ohci_data_t;
 
 //--------------------------------------------------------------------+
diff --git a/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c b/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c
index 21cd3da26..9c37f1f98 100644
--- a/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c
+++ b/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c
@@ -128,8 +128,8 @@
 #  define DCD_STM32_BTABLE_BASE 0U
 #endif
 
-#ifndef DCD_STM32_BTABLE_LENGTH
-#  define DCD_STM32_BTABLE_LENGTH (PMA_LENGTH - DCD_STM32_BTABLE_BASE)
+#ifndef DCD_STM32_BTABLE_SIZE
+#  define DCD_STM32_BTABLE_SIZE (FSDEV_PMA_SIZE - DCD_STM32_BTABLE_BASE)
 #endif
 
 /***************************************************
@@ -137,7 +137,7 @@
  */
 
 TU_VERIFY_STATIC((MAX_EP_COUNT) <= STFSDEV_EP_COUNT, "Only 8 endpoints supported on the hardware");
-TU_VERIFY_STATIC(((DCD_STM32_BTABLE_BASE) + (DCD_STM32_BTABLE_LENGTH))<=(PMA_LENGTH), "BTABLE does not fit in PMA RAM");
+TU_VERIFY_STATIC(((DCD_STM32_BTABLE_BASE) + (DCD_STM32_BTABLE_SIZE)) <= (FSDEV_PMA_SIZE), "BTABLE does not fit in PMA RAM");
 TU_VERIFY_STATIC(((DCD_STM32_BTABLE_BASE) % 8) == 0, "BTABLE base must be aligned to 8 bytes");
 
 //--------------------------------------------------------------------+
@@ -559,7 +559,7 @@ static void dcd_ep_ctr_rx_handler(uint32_t wIstr)
       // Must reset EP to NAK (in case it had been stalling) (though, maybe too late here)
       pcd_set_ep_rx_status(USB,0u,USB_EP_RX_NAK);
       pcd_set_ep_tx_status(USB,0u,USB_EP_TX_NAK);
-#ifdef PMA_32BIT_ACCESS
+#ifdef FSDEV_BUS_32BIT
       dcd_event_setup_received(0, (uint8_t*)(USB_PMAADDR + pcd_get_ep_rx_address(USB, EPindex)), true);
 #else
       // The setup_received function uses memcpy, so this must first copy the setup data into
@@ -673,13 +673,13 @@ void dcd_int_handler(uint8_t rhport) {
 
   /* Put SOF flag at the beginning of ISR in case to get least amount of jitter if it is used for timing purposes */
   if(int_status & USB_ISTR_SOF) {
-    USB->ISTR &=~USB_ISTR_SOF;
+    USB->ISTR = (fsdev_bus_t)~USB_ISTR_SOF;
     dcd_event_sof(0, USB->FNR & USB_FNR_FN, true);
   }
 
   if(int_status & USB_ISTR_RESET) {
     // USBRST is start of reset.
-    USB->ISTR &=~USB_ISTR_RESET;
+    USB->ISTR = (fsdev_bus_t)~USB_ISTR_RESET;
     dcd_handle_bus_reset();
     dcd_event_bus_reset(0, TUSB_SPEED_FULL, true);
     return; // Don't do the rest of the things here; perhaps they've been cleared?
@@ -697,7 +697,7 @@ void dcd_int_handler(uint8_t rhport) {
     USB->CNTR &= ~USB_CNTR_LPMODE;
     USB->CNTR &= ~USB_CNTR_FSUSP;
 
-    USB->ISTR &=~USB_ISTR_WKUP;
+    USB->ISTR = (fsdev_bus_t)~USB_ISTR_WKUP;
     dcd_event_bus_signal(0, DCD_EVENT_RESUME, true);
   }
 
@@ -711,7 +711,7 @@ void dcd_int_handler(uint8_t rhport) {
     USB->CNTR |= USB_CNTR_LPMODE;
 
     /* clear of the ISTR bit must be done after setting of CNTR_FSUSP */
-    USB->ISTR &=~USB_ISTR_SUSP;
+    USB->ISTR = (fsdev_bus_t)~USB_ISTR_SUSP;
     dcd_event_bus_signal(0, DCD_EVENT_SUSPEND, true);
   }
 
@@ -724,7 +724,7 @@ void dcd_int_handler(uint8_t rhport) {
     {
       remoteWakeCountdown--;
     }
-    USB->ISTR &=~USB_ISTR_ESOF;
+    USB->ISTR = (fsdev_bus_t)~USB_ISTR_ESOF;
   }
 }
 
@@ -786,7 +786,7 @@ static uint16_t dcd_pma_alloc(uint8_t ep_addr, uint16_t length)
   }
 
   // Ensure allocated buffer is aligned
-#ifdef PMA_32BIT_ACCESS
+#ifdef FSDEV_BUS_32BIT
   length = (length + 3) & ~0x03;
 #else
   length = (length + 1) & ~0x01;
@@ -798,7 +798,7 @@ static uint16_t dcd_pma_alloc(uint8_t ep_addr, uint16_t length)
   ep_buf_ptr = (uint16_t)(ep_buf_ptr + length); // increment buffer pointer
 
   // Verify no overflow
-  TU_ASSERT(ep_buf_ptr <= PMA_LENGTH, 0xFFFF);
+  TU_ASSERT(ep_buf_ptr <= FSDEV_PMA_SIZE, 0xFFFF);
 
   epXferCtl->pma_ptr = addr;
   epXferCtl->pma_alloc_size = length;
@@ -1227,7 +1227,7 @@ void dcd_edpt_clear_stall (uint8_t rhport, uint8_t ep_addr)
   }
 }
 
-#ifdef PMA_32BIT_ACCESS
+#ifdef FSDEV_BUS_32BIT
 static bool dcd_write_packet_memory(uint16_t dst, const void *__restrict src, uint16_t wNBytes)
 {
   const uint8_t* srcVal = src;
@@ -1283,7 +1283,7 @@ static bool dcd_write_packet_memory(uint16_t dst, const void *__restrict src, ui
   __IO uint16_t *pdwVal;
 
   srcVal = src;
-  pdwVal = &pma[PMA_STRIDE*(dst>>1)];
+  pdwVal = &pma[FSDEV_PMA_STRIDE * (dst >> 1)];
 
   while (n--)
   {
@@ -1291,7 +1291,7 @@ static bool dcd_write_packet_memory(uint16_t dst, const void *__restrict src, ui
     srcVal++;
     temp2 = temp1 | ((uint16_t)(((uint16_t)(*srcVal)) << 8U)) ;
     *pdwVal = temp2;
-    pdwVal += PMA_STRIDE;
+    pdwVal += FSDEV_PMA_STRIDE;
     srcVal++;
   }
 
@@ -1323,7 +1323,7 @@ static bool dcd_write_packet_memory_ff(tu_fifo_t * ff, uint16_t dst, uint16_t wN
   // We want to read from the FIFO and write it into the PMA, if LIN part is ODD and has WRAPPED part,
   // last lin byte will be combined with wrapped part
   // To ensure PMA is always access aligned (dst aligned to 16 or 32 bit)
-#ifdef PMA_32BIT_ACCESS
+#ifdef FSDEV_BUS_32BIT
   if((cnt_lin & 0x03) && cnt_wrap)
   {
     // Copy first linear part
@@ -1386,7 +1386,7 @@ static bool dcd_write_packet_memory_ff(tu_fifo_t * ff, uint16_t dst, uint16_t wN
   return true;
 }
 
-#ifdef PMA_32BIT_ACCESS
+#ifdef FSDEV_BUS_32BIT
 static bool dcd_read_packet_memory(void *__restrict dst, uint16_t src, uint16_t wNBytes)
 {
   uint8_t* dstVal = dst;
@@ -1434,13 +1434,13 @@ static bool dcd_read_packet_memory(void *__restrict dst, uint16_t src, uint16_t
   __IO const uint16_t *pdwVal;
   uint32_t temp;
 
-  pdwVal = &pma[PMA_STRIDE*(src>>1)];
+  pdwVal = &pma[FSDEV_PMA_STRIDE * (src >> 1)];
   uint8_t *dstVal = (uint8_t*)dst;
 
   while (n--)
   {
     temp = *pdwVal;
-    pdwVal += PMA_STRIDE;
+    pdwVal += FSDEV_PMA_STRIDE;
     *dstVal++ = ((temp >> 0) & 0xFF);
     *dstVal++ = ((temp >> 8) & 0xFF);
   }
@@ -1448,7 +1448,7 @@ static bool dcd_read_packet_memory(void *__restrict dst, uint16_t src, uint16_t
   if (wNBytes & 0x01)
   {
     temp = *pdwVal;
-    pdwVal += PMA_STRIDE;
+    pdwVal += FSDEV_PMA_STRIDE;
     *dstVal++ = ((temp >> 0) & 0xFF);
   }
   return true;
@@ -1475,7 +1475,7 @@ static bool dcd_read_packet_memory_ff(tu_fifo_t * ff, uint16_t src, uint16_t wNB
   // We want to read from PMA and write it into the FIFO, if LIN part is ODD and has WRAPPED part,
   // last lin byte will be combined with wrapped part
   // To ensure PMA is always access aligned (src aligned to 16 or 32 bit)
-#ifdef PMA_32BIT_ACCESS
+#ifdef FSDEV_BUS_32BIT
   if((cnt_lin & 0x03) && cnt_wrap)
   {
     // Copy first linear part
diff --git a/src/portable/st/stm32_fsdev/dcd_stm32_fsdev_pvt_st.h b/src/portable/st/stm32_fsdev/dcd_stm32_fsdev_pvt_st.h
index b71e4f498..3f4db985d 100644
--- a/src/portable/st/stm32_fsdev/dcd_stm32_fsdev_pvt_st.h
+++ b/src/portable/st/stm32_fsdev/dcd_stm32_fsdev_pvt_st.h
@@ -28,7 +28,7 @@
 
 // This file contains source copied from ST's HAL, and thus should have their copyright statement.
 
-// PMA_LENGTH is PMA buffer size in bytes.
+// FSDEV_PMA_SIZE is PMA buffer size in bytes.
 // On 512-byte devices, access with a stride of two words (use every other 16-bit address)
 // On 1024-byte devices, access with a stride of one word (use every 16-bit address)
 
@@ -37,7 +37,7 @@
 
 #if CFG_TUSB_MCU == OPT_MCU_STM32F0
   #include "stm32f0xx.h"
-  #define PMA_LENGTH (1024u)
+  #define FSDEV_PMA_SIZE (1024u)
   // F0x2 models are crystal-less
   // All have internal D+ pull-up
   // 070RB:    2 x 16 bits/word memory     LPM Support, BCD Support
@@ -45,7 +45,7 @@
 
 #elif CFG_TUSB_MCU == OPT_MCU_STM32F1
   #include "stm32f1xx.h"
-  #define PMA_LENGTH (512u)
+  #define FSDEV_PMA_SIZE (512u)
   // NO internal Pull-ups
   //         *B, and *C:    2 x 16 bits/word
 
@@ -56,7 +56,7 @@
       defined(STM32F303xB) || defined(STM32F303xC) || \
       defined(STM32F373xC)
   #include "stm32f3xx.h"
-  #define PMA_LENGTH (512u)
+  #define FSDEV_PMA_SIZE (512u)
   // NO internal Pull-ups
   //         *B, and *C:    1 x 16 bits/word
   // PMA dedicated to USB (no sharing with CAN)
@@ -65,27 +65,27 @@
       defined(STM32F302xD) || defined(STM32F302xE) || \
       defined(STM32F303xD) || defined(STM32F303xE)
   #include "stm32f3xx.h"
-  #define PMA_LENGTH (1024u)
+  #define FSDEV_PMA_SIZE (1024u)
   // NO internal Pull-ups
   // *6, *8, *D, and *E:    2 x 16 bits/word     LPM Support
   // When CAN clock is enabled, USB can use first 768 bytes ONLY.
 
 #elif CFG_TUSB_MCU == OPT_MCU_STM32L0
   #include "stm32l0xx.h"
-  #define PMA_LENGTH (1024u)
+  #define FSDEV_PMA_SIZE (1024u)
 
 #elif CFG_TUSB_MCU == OPT_MCU_STM32L1
   #include "stm32l1xx.h"
-  #define PMA_LENGTH (512u)
+  #define FSDEV_PMA_SIZE (512u)
 
 #elif CFG_TUSB_MCU == OPT_MCU_STM32G4
   #include "stm32g4xx.h"
-  #define PMA_LENGTH (1024u)
+  #define FSDEV_PMA_SIZE (1024u)
 
 #elif CFG_TUSB_MCU == OPT_MCU_STM32G0
   #include "stm32g0xx.h"
-  #define PMA_32BIT_ACCESS
-  #define PMA_LENGTH (2048u)
+  #define FSDEV_BUS_32BIT
+  #define FSDEV_PMA_SIZE (2048u)
   #undef USB_PMAADDR
   #define USB_PMAADDR USB_DRD_PMAADDR
   #define USB_TypeDef USB_DRD_TypeDef
@@ -112,8 +112,8 @@
 
 #elif CFG_TUSB_MCU == OPT_MCU_STM32H5
   #include "stm32h5xx.h"
-  #define PMA_32BIT_ACCESS
-  #define PMA_LENGTH (2048u)
+  #define FSDEV_BUS_32BIT
+  #define FSDEV_PMA_SIZE (2048u)
   #undef USB_PMAADDR
   #define USB_PMAADDR USB_DRD_PMAADDR
   #define USB_TypeDef USB_DRD_TypeDef
@@ -141,18 +141,18 @@
 
 #elif CFG_TUSB_MCU == OPT_MCU_STM32WB
   #include "stm32wbxx.h"
-  #define PMA_LENGTH (1024u)
+  #define FSDEV_PMA_SIZE (1024u)
   /* ST provided header has incorrect value */
   #undef USB_PMAADDR
   #define USB_PMAADDR USB1_PMAADDR
 
 #elif CFG_TUSB_MCU == OPT_MCU_STM32L4
   #include "stm32l4xx.h"
-  #define PMA_LENGTH (1024u)
+  #define FSDEV_PMA_SIZE (1024u)
 
 #elif CFG_TUSB_MCU == OPT_MCU_STM32L5
   #include "stm32l5xx.h"
-  #define PMA_LENGTH (1024u)
+  #define FSDEV_PMA_SIZE (1024u)
 
   #ifndef USB_PMAADDR
     #define USB_PMAADDR (USB_BASE + (USB_PMAADDR_NS - USB_BASE_NS))
@@ -164,24 +164,28 @@
 #endif
 
 // For purposes of accessing the packet
-#if ((PMA_LENGTH) == 512u)
-  #define PMA_STRIDE  (2u)
-#elif ((PMA_LENGTH) == 1024u)
-  #define PMA_STRIDE  (1u)
+#if ((FSDEV_PMA_SIZE) == 512u)
+  #define FSDEV_PMA_STRIDE  (2u)
+#elif ((FSDEV_PMA_SIZE) == 1024u)
+  #define FSDEV_PMA_STRIDE  (1u)
 #endif
 
+// The fsdev_bus_t type can be used for both register and PMA access necessities
 // For type-safety create a new macro for the volatile address of PMAADDR
 // The compiler should warn us if we cast it to a non-volatile type?
-#ifdef PMA_32BIT_ACCESS
+#ifdef FSDEV_BUS_32BIT
+typedef uint32_t fsdev_bus_t;
 static __IO uint32_t * const pma32 = (__IO uint32_t*)USB_PMAADDR;
+
 #else
+typedef uint16_t fsdev_bus_t;
 // Volatile is also needed to prevent the optimizer from changing access to 32-bit (as 32-bit access is forbidden)
 static __IO uint16_t * const pma = (__IO uint16_t*)USB_PMAADDR;
 
 TU_ATTR_ALWAYS_INLINE static inline __IO uint16_t * pcd_btable_word_ptr(USB_TypeDef * USBx, size_t x)
 {
   size_t total_word_offset = (((USBx)->BTABLE)>>1) + x;
-  total_word_offset *= PMA_STRIDE;
+  total_word_offset *= FSDEV_PMA_STRIDE;
   return &(pma[total_word_offset]);
 }
 
@@ -212,7 +216,7 @@ TU_ATTR_ALWAYS_INLINE static inline uint16_t pcd_aligned_buffer_size(uint16_t si
 /* SetENDPOINT */
 TU_ATTR_ALWAYS_INLINE static inline void pcd_set_endpoint(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t wRegValue)
 {
-#ifdef PMA_32BIT_ACCESS
+#ifdef FSDEV_BUS_32BIT
   (void) USBx;
   __O uint32_t *reg = (__O uint32_t *)(USB_DRD_BASE + bEpIdx*4);
   *reg = wRegValue;
@@ -224,7 +228,7 @@ TU_ATTR_ALWAYS_INLINE static inline void pcd_set_endpoint(USB_TypeDef * USBx, ui
 
 /* GetENDPOINT */
 TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_endpoint(USB_TypeDef * USBx, uint32_t bEpIdx) {
-#ifdef PMA_32BIT_ACCESS
+#ifdef FSDEV_BUS_32BIT
   (void) USBx;
   __I uint32_t *reg = (__I uint32_t *)(USB_DRD_BASE + bEpIdx*4);
 #else
@@ -279,7 +283,7 @@ TU_ATTR_ALWAYS_INLINE static inline void pcd_clear_tx_ep_ctr(USB_TypeDef * USBx,
   */
 TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_ep_tx_cnt(USB_TypeDef * USBx, uint32_t bEpIdx)
 {
-#ifdef PMA_32BIT_ACCESS
+#ifdef FSDEV_BUS_32BIT
   (void) USBx;
   return (pma32[2*bEpIdx] & 0x03FF0000) >> 16;
 #else
@@ -290,7 +294,7 @@ TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_ep_tx_cnt(USB_TypeDef * USB
 
 TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_ep_rx_cnt(USB_TypeDef * USBx, uint32_t bEpIdx)
 {
-#ifdef PMA_32BIT_ACCESS
+#ifdef FSDEV_BUS_32BIT
   (void) USBx;
   return (pma32[2*bEpIdx + 1] & 0x03FF0000) >> 16;
 #else
@@ -317,7 +321,7 @@ TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_address(USB_TypeDef * USBx,
 
 TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_ep_tx_address(USB_TypeDef * USBx, uint32_t bEpIdx)
 {
-#ifdef PMA_32BIT_ACCESS
+#ifdef FSDEV_BUS_32BIT
   (void) USBx;
   return pma32[2*bEpIdx] & 0x0000FFFFu ;
 #else
@@ -327,7 +331,7 @@ TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_ep_tx_address(USB_TypeDef *
 
 TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_ep_rx_address(USB_TypeDef * USBx, uint32_t bEpIdx)
 {
-#ifdef PMA_32BIT_ACCESS
+#ifdef FSDEV_BUS_32BIT
   (void) USBx;
   return pma32[2*bEpIdx + 1] & 0x0000FFFFu;
 #else
@@ -337,7 +341,7 @@ TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_ep_rx_address(USB_TypeDef *
 
 TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_tx_address(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t addr)
 {
-#ifdef PMA_32BIT_ACCESS
+#ifdef FSDEV_BUS_32BIT
   (void) USBx;
   pma32[2*bEpIdx] = (pma32[2*bEpIdx] & 0xFFFF0000u) | (addr & 0x0000FFFCu);
 #else
@@ -347,7 +351,7 @@ TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_tx_address(USB_TypeDef * USB
 
 TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_rx_address(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t addr)
 {
-#ifdef PMA_32BIT_ACCESS
+#ifdef FSDEV_BUS_32BIT
   (void) USBx;
   pma32[2*bEpIdx + 1] = (pma32[2*bEpIdx + 1] & 0xFFFF0000u) | (addr & 0x0000FFFCu);
 #else
@@ -357,7 +361,7 @@ TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_rx_address(USB_TypeDef * USB
 
 TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_tx_cnt(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t wCount)
 {
-#ifdef PMA_32BIT_ACCESS
+#ifdef FSDEV_BUS_32BIT
   (void) USBx;
   pma32[2*bEpIdx] = (pma32[2*bEpIdx] & ~0x03FF0000u) | ((wCount & 0x3FFu) << 16);
 #else
@@ -368,7 +372,7 @@ TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_tx_cnt(USB_TypeDef * USBx, u
 
 TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_rx_cnt(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t wCount)
 {
-#ifdef PMA_32BIT_ACCESS
+#ifdef FSDEV_BUS_32BIT
   (void) USBx;
   pma32[2*bEpIdx + 1] = (pma32[2*bEpIdx + 1] & ~0x03FF0000u) | ((wCount & 0x3FFu) << 16);
 #else
@@ -380,7 +384,7 @@ TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_rx_cnt(USB_TypeDef * USBx, u
 TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_blsize_num_blocks(USB_TypeDef * USBx, uint32_t rxtx_idx, uint32_t blocksize, uint32_t numblocks)
 {
   /* Encode into register. When BLSIZE==1, we need to subtract 1 block count */
-#ifdef PMA_32BIT_ACCESS
+#ifdef FSDEV_BUS_32BIT
   (void) USBx;
   pma32[rxtx_idx] = (pma32[rxtx_idx] & 0x0000FFFFu) | (blocksize << 31) | ((numblocks - blocksize) << 26);
 #else
diff --git a/src/portable/synopsys/dwc2/dwc2_stm32.h b/src/portable/synopsys/dwc2/dwc2_stm32.h
index dd78ccd06..3237a50f6 100644
--- a/src/portable/synopsys/dwc2/dwc2_stm32.h
+++ b/src/portable/synopsys/dwc2/dwc2_stm32.h
@@ -149,7 +149,7 @@ static inline void dwc2_phy_init(dwc2_regs_t* dwc2, uint8_t hs_phy_type) {
     // https://community.st.com/t5/stm32cubemx-mcus/why-stm32h743-usb-fs-doesn-t-work-if-freertos-tickless-idle/m-p/349480#M18867
     // H7 running on full-speed phy need to disable ULPI clock in sleep mode.
     // Otherwise, USB won't work when mcu executing WFI/WFE instruction i.e tick-less RTOS.
-    // Note: there may be other family that is affected by this, but only H7 is tested so far
+    // Note: there may be other family that is affected by this, but only H7 and F7 is tested so far
     #if defined(USB_OTG_FS_PERIPH_BASE) && defined(RCC_AHB1LPENR_USB2OTGFSULPILPEN)
     if ( USB_OTG_FS_PERIPH_BASE == (uint32_t) dwc2 ) {
       RCC->AHB1LPENR &= ~RCC_AHB1LPENR_USB2OTGFSULPILPEN;
@@ -161,6 +161,13 @@ static inline void dwc2_phy_init(dwc2_regs_t* dwc2, uint8_t hs_phy_type) {
       RCC->AHB1LPENR &= ~RCC_AHB1LPENR_USB1OTGHSULPILPEN;
     }
     #endif
+
+    #if defined(USB_OTG_HS_PERIPH_BASE) && defined(RCC_AHB1LPENR_OTGHSULPILPEN)
+    if ( USB_OTG_HS_PERIPH_BASE == (uint32_t) dwc2 ) {
+      RCC->AHB1LPENR &= ~RCC_AHB1LPENR_OTGHSULPILPEN;
+    }
+    #endif
+
   } else {
 #if CFG_TUSB_MCU != OPT_MCU_STM32U5
     // Disable FS PHY, TODO on U5A5 (dwc2 4.11a) 16th bit is 'Host CDP behavior enable'
diff --git a/src/tusb_option.h b/src/tusb_option.h
index a76281c0c..767323bdd 100644
--- a/src/tusb_option.h
+++ b/src/tusb_option.h
@@ -453,10 +453,12 @@
   #define CFG_TUH_CDC_FTDI 0
 #endif
 
-#ifndef CFG_TUH_CDC_FTDI_PID_LIST
-  // List of product IDs that can use the FTDI CDC driver
-  #define CFG_TUH_CDC_FTDI_PID_LIST \
-    0x6001, 0x6006, 0x6010, 0x6011, 0x6014, 0x6015, 0x8372, 0xFBFA, 0xCD18
+#ifndef CFG_TUH_CDC_FTDI_VID_PID_LIST
+  // List of product IDs that can use the FTDI CDC driver. 0x0403 is FTDI's VID
+  #define CFG_TUH_CDC_FTDI_VID_PID_LIST \
+    {0x0403, 0x6001}, {0x0403, 0x6006}, {0x0403, 0x6010}, {0x0403, 0x6011}, \
+    {0x0403, 0x6014}, {0x0403, 0x6015}, {0x0403, 0x8372}, {0x0403, 0xFBFA}, \
+    {0x0403, 0xCD18}
 #endif
 
 #ifndef CFG_TUH_CDC_CP210X
@@ -464,10 +466,27 @@
   #define CFG_TUH_CDC_CP210X 0
 #endif
 
-#ifndef CFG_TUH_CDC_CP210X_PID_LIST
-  // List of product IDs that can use the CP210X CDC driver
-  #define CFG_TUH_CDC_CP210X_PID_LIST \
-    0xEA60, 0xEA70
+#ifndef CFG_TUH_CDC_CP210X_VID_PID_LIST
+  // List of product IDs that can use the CP210X CDC driver. 0x10C4 is Silicon Labs' VID
+  #define CFG_TUH_CDC_CP210X_VID_PID_LIST \
+    {0x10C4, 0xEA60}, {0x10C4, 0xEA70}
+#endif
+
+#ifndef CFG_TUH_CDC_CH34X
+  // CH34X is not part of CDC class, only to re-use CDC driver API
+  #define CFG_TUH_CDC_CH34X 0
+#endif
+
+#ifndef CFG_TUH_CDC_CH34X_VID_PID_LIST
+  // List of product IDs that can use the CH34X CDC driver
+  #define CFG_TUH_CDC_CH34X_VID_PID_LIST \
+    { 0x1a86, 0x5523 }, /* ch341 chip */ \
+    { 0x1a86, 0x7522 }, /* ch340k chip */ \
+    { 0x1a86, 0x7523 }, /* ch340 chip */ \
+    { 0x1a86, 0xe523 }, /* ch330 chip */ \
+    { 0x4348, 0x5523 }, /* ch340 custom chip */ \
+    { 0x2184, 0x0057 }, /* overtaken from Linux Kernel driver /drivers/usb/serial/ch341.c */ \
+    { 0x9986, 0x7523 }  /* overtaken from Linux Kernel driver /drivers/usb/serial/ch341.c */
 #endif
 
 #ifndef CFG_TUH_HID
diff --git a/test/hil/hil_test.py b/test/hil/hil_test.py
index 7c662aeea..7f03ce43f 100644
--- a/test/hil/hil_test.py
+++ b/test/hil/hil_test.py
@@ -364,8 +364,15 @@ def main(config_file, board):
 
             print(f'  {test} ...', end='')
 
-            # flash firmware
-            ret = globals()[f'flash_{flasher}'](item, fw)
+            # flash firmware. It may fail randomly, retry a few times
+            for i in range(3):
+                ret = globals()[f'flash_{flasher}'](item, fw)
+                if ret.returncode == 0:
+                    break
+                else:
+                    print(f'Flashing failed, retry {i+1}')
+                    time.sleep(1)
+
             assert ret.returncode == 0, 'Flash failed\n' + ret.stdout.decode()
 
             # run test