From 4e6408ea4940085f66c676772d77e3951e82ea11 Mon Sep 17 00:00:00 2001
From: IngHK <github@hkue.de>
Date: Tue, 16 Jan 2024 08:07:22 +0100
Subject: [PATCH] CDCh host: further work on CH340/CH341 support

---
 examples/host/cdc_msc_hid/src/tusb_config.h   |   3 +-
 .../cdc_msc_hid_freertos/src/tusb_config.h    |   3 +-
 src/class/cdc/cdc_debug.h                     |  65 ++
 src/class/cdc/cdc_device.c                    |  21 +-
 src/class/cdc/cdc_host.c                      | 570 ++++++++----------
 src/class/cdc/cdc_host.h                      |   4 +-
 src/class/cdc/serial/ch34x.h                  | 103 +---
 7 files changed, 370 insertions(+), 399 deletions(-)
 create mode 100644 src/class/cdc/cdc_debug.h

diff --git a/examples/host/cdc_msc_hid/src/tusb_config.h b/examples/host/cdc_msc_hid/src/tusb_config.h
index 76d59c316..ac14ef97e 100644
--- a/examples/host/cdc_msc_hid/src/tusb_config.h
+++ b/examples/host/cdc_msc_hid/src/tusb_config.h
@@ -120,8 +120,7 @@
 //------------- CDC -------------//
 
 // Set Line Control state on enumeration/mounted:
-// DTR ( bit 0), RTS (bit 1)
-#define CFG_TUH_CDC_LINE_CONTROL_ON_ENUM    0x03
+#define CFG_TUH_CDC_LINE_CONTROL_ON_ENUM ( CDC_CONTROL_LINE_STATE_RTS | CDC_CONTROL_LINE_STATE_DTR )
 
 // Set Line Coding on enumeration/mounted, value for cdc_line_coding_t
 // bit rate = 115200, 1 stop bit, no parity, 8 bit data width
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 bb7c3388d..1b68dd2d8 100644
--- a/examples/host/cdc_msc_hid_freertos/src/tusb_config.h
+++ b/examples/host/cdc_msc_hid_freertos/src/tusb_config.h
@@ -125,8 +125,7 @@
 //------------- CDC -------------//
 
 // Set Line Control state on enumeration/mounted:
-// DTR ( bit 0), RTS (bit 1)
-#define CFG_TUH_CDC_LINE_CONTROL_ON_ENUM    0x03
+#define CFG_TUH_CDC_LINE_CONTROL_ON_ENUM ( CDC_CONTROL_LINE_STATE_RTS | CDC_CONTROL_LINE_STATE_DTR )
 
 // Set Line Coding on enumeration/mounted, value for cdc_line_coding_t
 // bit rate = 115200, 1 stop bit, no parity, 8 bit data width
diff --git a/src/class/cdc/cdc_debug.h b/src/class/cdc/cdc_debug.h
new file mode 100644
index 000000000..879e49a50
--- /dev/null
+++ b/src/class/cdc/cdc_debug.h
@@ -0,0 +1,65 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2023 Ha Thach (tinyusb.org)
+ * Copyright (c) 2023 IngHK Heiko Kuester
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#ifndef _TUSB_CDC_DEBUG_H_
+#define _TUSB_CDC_DEBUG_H_
+
+#include "cdc.h"
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+// logging of line coding
+#define TU_LOG_LINE_CODING(PRE_TEXT,LINE_CODING)                            \
+  TU_LOG_DRV(PRE_TEXT "Line Coding %lu Bd, %u%c%s\r\n",                     \
+    LINE_CODING.bit_rate,                                                   \
+    LINE_CODING.data_bits,                                                  \
+    LINE_CODING.parity == CDC_LINE_CODING_PARITY_NONE ? 'N' :               \
+      LINE_CODING.parity == CDC_LINE_CODING_PARITY_ODD ? 'O' :              \
+        LINE_CODING.parity == CDC_LINE_CODING_PARITY_EVEN ? 'E' :           \
+          LINE_CODING.parity == CDC_LINE_CODING_PARITY_MARK ? 'M' :         \
+            LINE_CODING.parity == CDC_LINE_CODING_PARITY_SPACE ? 'S' : '?', \
+    LINE_CODING.stop_bits == CDC_LINE_CODING_STOP_BITS_1 ? "1" :            \
+      LINE_CODING.stop_bits == CDC_LINE_CODING_STOP_BITS_1_5 ? "1.5" :      \
+        LINE_CODING.stop_bits == CDC_LINE_CODING_STOP_BITS_2 ? "2" : "?")
+
+// logging of baudrate
+#define TU_LOG_BAUDRATE(PRE_TEXT,BAUDRATE) \
+  TU_LOG_DRV(PRE_TEXT "Baudrate %lu Bd\r\n", BAUDRATE)
+
+ // logging of control line state
+#define TU_LOG_CONTROL_LINE_STATE(PRE_TEXT,LINE_STATE)            \
+  TU_LOG_DRV(PRE_TEXT "Control Line State RTS%c DTR%c\r\n", \
+    LINE_STATE & CDC_CONTROL_LINE_STATE_RTS ? '+' : '-',           \
+    LINE_STATE & CDC_CONTROL_LINE_STATE_DTR ? '+' : '-' )
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif /* _TUSB_CDC_DEBUG_H_ */
diff --git a/src/class/cdc/cdc_device.c b/src/class/cdc/cdc_device.c
index c26264e60..b763419e7 100644
--- a/src/class/cdc/cdc_device.c
+++ b/src/class/cdc/cdc_device.c
@@ -32,6 +32,9 @@
 #include "device/usbd_pvt.h"
 
 #include "cdc_device.h"
+#if 0 // TODO activate and test
+#include "cdc_debug.h"
+#endif
 
 // Level where CFG_TUSB_DEBUG must be at least for this driver is logged
 #ifndef CFG_TUD_CDC_LOG_LEVEL
@@ -360,7 +363,11 @@ bool cdcd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t
     case CDC_REQUEST_SET_LINE_CODING:
       if (stage == CONTROL_STAGE_SETUP)
       {
-        TU_LOG_DRV("  Set Line Coding\r\n");
+        #if 0 // TODO activate, test and remove else
+          TU_LOG_LINE_CODING("  Set ", p_cdc->line_coding);
+        #else
+          TU_LOG_DRV("  Set Line Coding\r\n");
+        #endif
         tud_control_xfer(rhport, request, &p_cdc->line_coding, sizeof(cdc_line_coding_t));
       }
       else if ( stage == CONTROL_STAGE_ACK)
@@ -372,7 +379,11 @@ bool cdcd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t
     case CDC_REQUEST_GET_LINE_CODING:
       if (stage == CONTROL_STAGE_SETUP)
       {
-        TU_LOG_DRV("  Get Line Coding\r\n");
+        #if 0 // TODO activate, test and remove else
+          TU_LOG_LINE_CODING("  Get ", p_cdc->line_coding);
+        #else
+          TU_LOG_DRV("  Get Line Coding\r\n");
+        #endif
         tud_control_xfer(rhport, request, &p_cdc->line_coding, sizeof(cdc_line_coding_t));
       }
     break;
@@ -397,7 +408,11 @@ bool cdcd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t
         // Disable fifo overwriting if DTR bit is set
         tu_fifo_set_overwritable(&p_cdc->tx_ff, !dtr);
 
-        TU_LOG_DRV("  Set Control Line State: DTR = %d, RTS = %d\r\n", dtr, rts);
+        #if 0 // TODO activate, test and remove else
+          TU_LOG_CONTROL_LINE_STATE("  Set ", p_cdc->line_state);
+        #else
+          TU_LOG_DRV("  Set Control Line State: DTR = %d, RTS = %d\r\n", dtr, rts);
+        #endif
 
         // Invoke callback
         if ( tud_cdc_line_state_cb ) tud_cdc_line_state_cb(itf, dtr, rts);
diff --git a/src/class/cdc/cdc_host.c b/src/class/cdc/cdc_host.c
index f6a6773e0..d46bfecb4 100644
--- a/src/class/cdc/cdc_host.c
+++ b/src/class/cdc/cdc_host.c
@@ -2,7 +2,7 @@
  * The MIT License (MIT)
  *
  * Copyright (c) 2019 Ha Thach (tinyusb.org)
- * Copyright (c) 2023 Heiko Kuester (CH34x support)
+ * Copyright (c) 2023 IngHK Heiko Kuester
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * of this software and associated documentation files (the "Software"), to deal
@@ -33,6 +33,7 @@
 #include "host/usbh_pvt.h"
 
 #include "cdc_host.h"
+#include "cdc_debug.h"
 
 // Level where CFG_TUSB_DEBUG must be at least for this driver is logged
 #ifndef CFG_TUH_CDC_LOG_LEVEL
@@ -55,7 +56,7 @@ typedef struct {
   cdc_acm_capability_t acm_capability;
   uint8_t ep_notif;
 
-  uint8_t line_state;                               // DTR (bit0), RTS (bit1)
+  uint8_t line_state;                               // RTS, DTR (refer enums CDC_CONTROL_LINE_STATE_RTS, CDC_CONTROL_LINE_STATE_DTR)
   TU_ATTR_ALIGNED(4) cdc_line_coding_t line_coding; // Baudrate, stop bits, parity, data width
 
   tuh_xfer_cb_t user_control_cb;
@@ -70,16 +71,9 @@ 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;
-  #if CFG_TUH_CDC_CH34X
-  struct {
-    uint32_t  baud_rate;
-    uint8_t   mcr;
-    uint8_t   msr;
-    uint8_t   lcr;
-    uint32_t  quirks;
-    uint8_t   version;
-  } ch34x;
-  #endif
+#if CFG_TUH_CDC_FTDI || CFG_TUH_CDC_CH34X
+  uint32_t  baudrate_requested;
+#endif
 } cdch_interface_t;
 
 CFG_TUH_MEM_SECTION
@@ -106,12 +100,10 @@ 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 bool ftdi_open(uint8_t daddr, const tusb_desc_interface_t *itf_desc, uint16_t max_len);
 static void ftdi_process_config(tuh_xfer_t* xfer);
 
+static bool ftdi_set_line_coding(cdch_interface_t* p_cdc, cdc_line_coding_t const* line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
 static bool ftdi_sio_set_modem_ctrl(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
 static bool ftdi_sio_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
 #endif
@@ -128,6 +120,7 @@ enum {
 static bool cp210x_open(uint8_t daddr, tusb_desc_interface_t const *itf_desc, uint16_t max_len);
 static void cp210x_process_config(tuh_xfer_t* xfer);
 
+static bool cp210x_set_line_coding(cdch_interface_t* p_cdc, cdc_line_coding_t const* line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
 static bool cp210x_set_modem_ctrl(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
 static bool cp210x_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
 #endif
@@ -144,6 +137,7 @@ enum {
 static bool ch34x_open ( uint8_t daddr, tusb_desc_interface_t const *itf_desc, uint16_t max_len );
 static void ch34x_process_config ( tuh_xfer_t* xfer );
 
+static bool ch34x_set_line_coding(cdch_interface_t* p_cdc, cdc_line_coding_t const* line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
 static bool ch34x_set_modem_ctrl ( cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data );
 static bool ch34x_set_baudrate ( cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data );
 #endif
@@ -169,35 +163,40 @@ typedef struct {
   void (*const process_set_config)(tuh_xfer_t* xfer);
   bool (*const set_control_line_state)(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
   bool (*const set_baudrate)(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+  bool (*const set_line_coding)(cdch_interface_t* p_cdc, cdc_line_coding_t const* line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
 } cdch_serial_driver_t;
 
 // Note driver list must be in the same order as SERIAL_DRIVER enum
 static const cdch_serial_driver_t serial_drivers[] = {
   { .process_set_config     = acm_process_config,
     .set_control_line_state = acm_set_control_line_state,
-    .set_baudrate           = acm_set_baudrate
+    .set_baudrate           = acm_set_baudrate,
+    .set_line_coding        = acm_set_line_coding
   },
 
   #if CFG_TUH_CDC_FTDI
   { .process_set_config     = ftdi_process_config,
     .set_control_line_state = ftdi_sio_set_modem_ctrl,
-    .set_baudrate           = ftdi_sio_set_baudrate
+    .set_baudrate           = ftdi_sio_set_baudrate,
+    .set_line_coding        = ftdi_set_line_coding
   },
   #endif
 
   #if CFG_TUH_CDC_CP210X
   { .process_set_config     = cp210x_process_config,
     .set_control_line_state = cp210x_set_modem_ctrl,
-    .set_baudrate           = cp210x_set_baudrate
+    .set_baudrate           = cp210x_set_baudrate,
+    .set_line_coding        = cp210x_set_line_coding
   },
   #endif
 
   #if CFG_TUH_CDC_CH34X
   { .process_set_config     = ch34x_process_config,
     .set_control_line_state = ch34x_set_modem_ctrl,
-    .set_baudrate           = ch34x_set_baudrate
+    .set_baudrate           = ch34x_set_baudrate,
+    .set_line_coding        = ch34x_set_line_coding
   },
-#endif
+  #endif
 };
 
 enum {
@@ -304,6 +303,7 @@ bool tuh_cdc_get_dtr(uint8_t idx)
 {
   cdch_interface_t* p_cdc = get_itf(idx);
   TU_VERIFY(p_cdc);
+  TU_LOG_CONTROL_LINE_STATE("CDCh Local ", p_cdc->line_state);
 
   return (p_cdc->line_state & CDC_CONTROL_LINE_STATE_DTR) ? true : false;
 }
@@ -312,6 +312,7 @@ bool tuh_cdc_get_rts(uint8_t idx)
 {
   cdch_interface_t* p_cdc = get_itf(idx);
   TU_VERIFY(p_cdc);
+  TU_LOG_CONTROL_LINE_STATE("CDCh Local ", p_cdc->line_state);
 
   return (p_cdc->line_state & CDC_CONTROL_LINE_STATE_RTS) ? true : false;
 }
@@ -322,6 +323,7 @@ bool tuh_cdc_get_local_line_coding(uint8_t idx, cdc_line_coding_t* line_coding)
   TU_VERIFY(p_cdc);
 
   *line_coding = p_cdc->line_coding;
+  TU_LOG_LINE_CODING("CDCh Get ", p_cdc->line_coding);
 
   return true;
 }
@@ -440,7 +442,7 @@ static void cdch_internal_control_complete(tuh_xfer_t* xfer)
 
           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->baudrate_requested;
             break;
 
           default: break;
@@ -465,12 +467,6 @@ static void cdch_internal_control_complete(tuh_xfer_t* xfer)
         break;
       #endif
 
-      #if CFG_TUH_CDC_CH34X
-      case SERIAL_DRIVER_CH34X:
-        TU_ASSERT(false, ); // see special ch34x_control_complete function
-        break;
-      #endif
-
       default: break;
     }
   }
@@ -487,7 +483,7 @@ bool tuh_cdc_set_control_line_state(uint8_t idx, uint16_t line_state, tuh_xfer_c
   cdch_serial_driver_t const* driver = &serial_drivers[p_cdc->serial_drid];
 
   if ( complete_cb ) {
-    return driver->set_control_line_state(p_cdc, line_state, complete_cb, user_data);
+    TU_VERIFY(driver->set_control_line_state(p_cdc, line_state, complete_cb, user_data));
   }else {
     // blocking
     xfer_result_t result = XFER_RESULT_INVALID;
@@ -501,8 +497,10 @@ 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;
   }
+  TU_LOG_CONTROL_LINE_STATE("CDCh Set ", p_cdc->line_state);
+
+  return true;
 }
 
 bool tuh_cdc_set_baudrate(uint8_t idx, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
@@ -511,7 +509,7 @@ bool tuh_cdc_set_baudrate(uint8_t idx, uint32_t baudrate, tuh_xfer_cb_t complete
   cdch_serial_driver_t const* driver = &serial_drivers[p_cdc->serial_drid];
 
   if ( complete_cb ) {
-    return driver->set_baudrate(p_cdc, baudrate, complete_cb, user_data);
+    TU_VERIFY(driver->set_baudrate(p_cdc, baudrate, complete_cb, user_data));
   }else {
     // blocking
     xfer_result_t result = XFER_RESULT_INVALID;
@@ -525,23 +523,24 @@ 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;
   }
+  TU_LOG_BAUDRATE("CDCh Set ", p_cdc->line_coding.bit_rate);
+
+  return true;
 }
 
 bool tuh_cdc_set_line_coding(uint8_t idx, cdc_line_coding_t const* line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data)
 {
   cdch_interface_t* p_cdc = get_itf(idx);
-  // 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 {
+    TU_VERIFY(driver->set_line_coding(p_cdc, line_coding, complete_cb, user_data));
+  } else {
     // blocking
     xfer_result_t result = XFER_RESULT_INVALID;
-    bool ret = acm_set_line_coding(p_cdc, line_coding, complete_cb, (uintptr_t) &result);
+    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
@@ -551,8 +550,10 @@ 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 = *line_coding;
-    return true;
   }
+  TU_LOG_LINE_CODING("CDCh Set ", p_cdc->line_coding);
+
+  return true;
 }
 
 //--------------------------------------------------------------------+
@@ -684,13 +685,8 @@ bool cdch_open(uint8_t rhport, uint8_t daddr, tusb_desc_interface_t const *itf_d
   {
     return acm_open(daddr, itf_desc, max_len);
   }
-<<<<<<< HEAD
   #if CFG_TUH_CDC_FTDI || CFG_TUH_CDC_CP210X || CFG_TUH_CDC_CH34X
-  else if ( 0xff == itf_desc->bInterfaceClass )
-=======
-  #if CFG_TUH_CDC_FTDI || CFG_TUH_CDC_CP210X
   else if ( TUSB_CLASS_VENDOR_SPECIFIC == itf_desc->bInterfaceClass )
->>>>>>> remotes/hathach/master
   {
     uint16_t vid, pid;
     TU_VERIFY(tuh_vid_pid_get(daddr, &vid, &pid));
@@ -816,6 +812,8 @@ static bool acm_open(uint8_t daddr, tusb_desc_interface_t const *itf_desc, uint1
     TU_ASSERT(open_ep_stream_pair(p_cdc, (tusb_desc_endpoint_t const *) p_desc));
   }
 
+  TU_LOG_DRV("[%u] CDCh ACM opened\r\n", daddr);
+
   return true;
 }
 
@@ -852,6 +850,12 @@ static void acm_process_config(tuh_xfer_t* xfer)
       TU_ATTR_FALLTHROUGH;
 
     case CONFIG_ACM_COMPLETE:
+      #ifdef CFG_TUH_CDC_LINE_CODING_ON_ENUM
+        TU_LOG_LINE_CODING("CDCh ACM Init ", p_cdc->line_coding);
+      #endif
+      #if CFG_TUH_CDC_LINE_CONTROL_ON_ENUM
+        TU_LOG_CONTROL_LINE_STATE("CDCh ACM Init", p_cdc->line_state);
+      #endif
       // itf_num+1 to account for data interface as well
       set_config_complete(p_cdc, idx, itf_num+1);
       break;
@@ -893,6 +897,7 @@ static bool acm_set_control_line_state(cdch_interface_t* p_cdc, uint16_t line_st
 
 static bool acm_set_line_coding(cdch_interface_t* p_cdc, cdc_line_coding_t const* line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
   TU_LOG_DRV("CDC ACM Set Line Conding\r\n");
+  TU_VERIFY(p_cdc->acm_capability.support_line_request);
 
   tusb_control_request_t const request = {
     .bmRequestType_bit = {
@@ -994,6 +999,12 @@ static bool ftdi_sio_reset(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, u
   return ftdi_sio_set_request(p_cdc, FTDI_SIO_RESET, FTDI_SIO_RESET_SIO, complete_cb, user_data);
 }
 
+static bool ftdi_set_line_coding(cdch_interface_t* p_cdc, cdc_line_coding_t const* line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data)
+{
+  TU_ASSERT(false, false); // TODO
+  return false;
+}
+
 static bool ftdi_sio_set_modem_ctrl(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data)
 {
   TU_LOG_DRV("CDC FTDI Set Control Line State\r\n");
@@ -1035,7 +1046,7 @@ static bool ftdi_sio_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, tu
   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->baudrate_requested = baudrate;
   TU_ASSERT(ftdi_sio_set_request(p_cdc, FTDI_SIO_SET_BAUD_RATE, divisor,
                                  complete_cb ? cdch_internal_control_complete : NULL, user_data));
 
@@ -1088,6 +1099,14 @@ static void ftdi_process_config(tuh_xfer_t* xfer) {
 
     case CONFIG_FTDI_COMPLETE:
       set_config_complete(p_cdc, idx, itf_num);
+      #if 0 // TODO set data format
+        #ifdef CFG_TUH_CDC_LINE_CODING_ON_ENUM
+          TU_LOG_LINE_CODING("CDCh FTDI Init ", p_cdc->line_coding);
+        #endif
+      #endif
+      #if CFG_TUH_CDC_LINE_CONTROL_ON_ENUM
+        TU_LOG_CONTROL_LINE_STATE("CDCh FTDI Init ", p_cdc->line_state);
+      #endif
       break;
 
     default:
@@ -1166,6 +1185,12 @@ 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)
+{
+  TU_ASSERT(false, false); // TODO
+  return false;
+}
+
 static bool cp210x_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
   TU_LOG_DRV("CDC CP210x Set BaudRate = %lu\r\n", baudrate);
   uint32_t baud_le = tu_htole32(baudrate);
@@ -1224,6 +1249,12 @@ static void cp210x_process_config(tuh_xfer_t* xfer) {
 
     case CONFIG_CP210X_COMPLETE:
       set_config_complete(p_cdc, idx, itf_num);
+      #if defined(CFG_TUH_CDC_LINE_CODING_ON_ENUM) && 0 // skip for now
+        TU_LOG_LINE_CODING("CDCh CP210x Init ", p_cdc->line_coding);
+      #endif
+      #if CFG_TUH_CDC_LINE_CONTROL_ON_ENUM
+        TU_LOG_CONTROL_LINE_STATE("CDCh CP210x Init ", p_cdc->line_state);
+      #endif
       break;
 
     default: break;
@@ -1233,20 +1264,19 @@ static void cp210x_process_config(tuh_xfer_t* xfer) {
 #endif
 
 //--------------------------------------------------------------------+
-// CH34x
+// CH34x (CH340 & CH341)
 //--------------------------------------------------------------------+
 
 #if CFG_TUH_CDC_CH34X
 
+#define CH34X_LOGS false
+
 enum {
-  CONFIG_CH34X_STEP1 = 0,
-  CONFIG_CH34X_STEP2,
-  CONFIG_CH34X_STEP3,
-  CONFIG_CH34X_STEP4,
-  CONFIG_CH34X_STEP5,
-  CONFIG_CH34X_STEP6,
-  CONFIG_CH34X_STEP7,
-  CONFIG_CH34X_STEP8,
+  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
 };
 
@@ -1259,17 +1289,22 @@ static bool ch34x_open ( uint8_t daddr, tusb_desc_interface_t const *itf_desc, u
   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;
 
   // endpoint pair
   tusb_desc_endpoint_t const * desc_ep = (tusb_desc_endpoint_t const *) tu_desc_next ( itf_desc );
 
   // data endpoints expected to be in pairs
-  return open_ep_stream_pair ( p_cdc, desc_ep );
+  TU_ASSERT(open_ep_stream_pair(p_cdc, desc_ep), false);
+
+  TU_LOG_DRV("[%u] CDCh CH34x opened\r\n", daddr);
+
+  return true;
 }
 
-static bool ch34x_set_request ( cdch_interface_t* p_cdc, uint8_t direction, uint8_t request, uint16_t value, uint16_t index, uint8_t* buffer, uint16_t length, tuh_xfer_cb_t complete_cb, uintptr_t user_data )
+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 = {
@@ -1297,212 +1332,66 @@ static bool ch34x_set_request ( cdch_interface_t* p_cdc, uint8_t direction, uint
       .setup       = &request_setup,
       .buffer      = enum_buf,
       .complete_cb = complete_cb,
-      // CH34x needs a special handling of bInterfaceNumber, because wIndex is used for other purposes and not for bInterfaceNumber
-      .user_data   = (uintptr_t)( ( p_cdc->bInterfaceNumber & 0xff ) << 8 ) | ( user_data & 0xff )
+      .user_data   = user_data
   };
 
   return tuh_control_xfer ( &xfer );
-  return false;
 }
 
-static bool ch341_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 )
+static bool ch34x_control_out ( cdch_interface_t* p_cdc, uint8_t request, uint16_t value, uint16_t index, tuh_xfer_cb_t complete_cb, uintptr_t user_data )
 {
   return ch34x_set_request ( p_cdc, TUSB_DIR_OUT, request, value, index, /* buffer */ NULL, /* length */ 0, complete_cb, user_data );
 }
 
-static bool ch341_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 )
+static bool ch34x_control_in ( cdch_interface_t* p_cdc, uint8_t request, uint16_t value, uint16_t index,
+                               uint8_t *buffer, uint16_t buffersize, tuh_xfer_cb_t complete_cb, uintptr_t user_data )
 {
   return ch34x_set_request ( p_cdc, TUSB_DIR_IN, request, value, index, buffer, buffersize, complete_cb, user_data );
 }
 
-static int32_t ch341_write_reg ( cdch_interface_t* p_cdc, uint16_t reg, uint16_t value, tuh_xfer_cb_t complete_cb, uintptr_t user_data )
+static bool ch34x_write_reg ( cdch_interface_t* p_cdc, uint16_t reg, uint16_t value, tuh_xfer_cb_t complete_cb, uintptr_t user_data )
 {
-  return ch341_control_out ( p_cdc, CH341_REQ_WRITE_REG, /* value */ reg, /* index */ value, complete_cb, user_data );
+  return ch34x_control_out ( p_cdc, CH34X_REQ_WRITE_REG, /* value */ reg, /* index */ value, complete_cb, user_data );
 }
 
-static int32_t ch341_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 )
+//static bool ch34x_read_reg_request ( cdch_interface_t* p_cdc, uint16_t reg,
+//                                     uint8_t *buffer, uint16_t buffersize, tuh_xfer_cb_t complete_cb, uintptr_t user_data )
+//{
+//  return ch34x_control_in ( p_cdc, CH34X_REQ_READ_REG, reg, /* index */ 0, buffer, buffersize, complete_cb, user_data );
+//}
+
+uint8_t ch34x_xfer_get_itf_num ( tuh_xfer_t* xfer )
+// CH34x needs a special handling to get bInterfaceNumber, because wIndex is used for other purposes and not for bInterfaceNumber
+// CH340 and CH341 derivates have always only one interface, so it's OK to check only daddr
 {
-  return ch341_control_in ( p_cdc, CH341_REQ_READ_REG, reg, /* index */ 0, buffer, buffersize, complete_cb, user_data );
-}
-
-/*
- * The device line speed is given by the following equation:
- *
- *  baudrate = 48000000 / (2^(12 - 3 * ps - fact) * div), where
- *
- *    0 <= ps <= 3,
- *    0 <= fact <= 1,
- *    2 <= div <= 256 if fact = 0, or
- *    9 <= div <= 256 if fact = 1
- */
-// calculate baudrate devisors
-// Parts of this functions have been taken over from Linux driver /drivers/usb/serial/ch341.c
-static int32_t ch341_get_divisor ( cdch_interface_t* p_cdc, uint32_t speed )
-{
-  uint32_t fact, div, clk_div;
-  bool force_fact0 = false;
-  int32_t ps;
-  static const uint32_t ch341_min_rates[] = {
-      CH341_MIN_RATE(0),
-      CH341_MIN_RATE(1),
-      CH341_MIN_RATE(2),
-      CH341_MIN_RATE(3),
-  };
-
-  /*
-   * Clamp to supported range, this makes the (ps < 0) and (div < 2)
-   * sanity checks below redundant.
-   */
-  inline uint32_t max       ( uint32_t val,                  uint32_t maxval ) { return val > maxval ? val : maxval; }
-  inline uint32_t min       ( uint32_t val, uint32_t minval                  ) { return val < minval ? val : minval; }
-  inline uint32_t clamp_val ( uint32_t val, uint32_t minval, uint32_t maxval ) { return min ( max ( val, minval ), maxval ); }
-  speed = clamp_val(speed, CH341_MIN_BPS, CH341_MAX_BPS);
-
-  /*
-   * Start with highest possible base clock (fact = 1) that will give a
-   * divisor strictly less than 512.
-   */
-  fact = 1;
-  for (ps = 3; ps >= 0; ps--) {
-    if (speed > ch341_min_rates[ps])
-      break;
-  }
-
-  if (ps < 0)
-    return -EINVAL;
-
-  /* Determine corresponding divisor, rounding down. */
-  clk_div = CH341_CLK_DIV(ps, fact);
-  div = CH341_CLKRATE / (clk_div * speed);
-
-  /* Some devices require a lower base clock if ps < 3. */
-  if (ps < 3 && (p_cdc->ch34x.quirks & CH341_QUIRK_LIMITED_PRESCALER))
-    force_fact0 = true;
-
-  /* Halve base clock (fact = 0) if required. */
-  if (div < 9 || div > 255 || force_fact0) {
-    div /= 2;
-    clk_div *= 2;
-    fact = 0;
-  }
-
-  if (div < 2)
-    return -EINVAL;
-
-  /*
-   * Pick next divisor if resulting rate is closer to the requested one,
-   * scale up to avoid rounding errors on low rates.
-   */
-  if (16 * CH341_CLKRATE / (clk_div * div) - 16 * speed >=
-      16 * speed - 16 * CH341_CLKRATE / (clk_div * (div + 1)))
-    div++;
-
-  /*
-   * Prefer lower base clock (fact = 0) if even divisor.
-   *
-   * Note that this makes the receiver more tolerant to errors.
-   */
-  if (fact == 1 && div % 2 == 0) {
-    div /= 2;
-    fact = 0;
-  }
-
-  return (0x100 - div) << 8 | fact << 2 | ps;
-}
-
-// set baudrate (low level)
-// do not confuse with ch34x_set_baudrate
-// Parts of this functions have been taken over from Linux driver /drivers/usb/serial/ch341.c
-static int32_t ch341_set_baudrate ( cdch_interface_t* p_cdc, uint32_t baud_rate, tuh_xfer_cb_t complete_cb, uintptr_t user_data )
-{
-  int val;
-
-  if (!baud_rate)
-    return -EINVAL;
-
-  val = ch341_get_divisor(p_cdc, baud_rate);
-  if (val < 0)
-    return -EINVAL;
-
-  /*
-   * CH341A buffers data until a full endpoint-size packet (32 bytes)
-   * has been received unless bit 7 is set.
-   *
-   * At least one device with version 0x27 appears to have this bit
-   * inverted.
-   */
-  if ( p_cdc->ch34x.version > 0x27 )
-    val |= BIT(7);
-
-  return ch341_write_reg ( p_cdc, CH341_REG_DIVISOR << 8 | CH341_REG_PRESCALER, val, complete_cb, user_data );
-}
-
-// set lcr register
-// Parts of this functions have been taken over from Linux driver /drivers/usb/serial/ch341.c
-static int32_t ch341_set_lcr ( cdch_interface_t* p_cdc, uint8_t lcr, tuh_xfer_cb_t complete_cb, uintptr_t user_data )
-{
-  /*
-   * Chip versions before version 0x30 as read using
-   * CH341_REQ_READ_VERSION used separate registers for line control
-   * (stop bits, parity and word length). Version 0x30 and above use
-   * CH341_REG_LCR only and CH341_REG_LCR2 is always set to zero.
-   */
-  if ( p_cdc->ch34x.version < 0x30 )
-    return 0;
-
-  return ch341_write_reg ( p_cdc, CH341_REG_LCR2 << 8 | CH341_REG_LCR, lcr, complete_cb, user_data );
-}
-
-// set handshake (modem controls)
-// Parts of this functions have been taken over from Linux driver /drivers/usb/serial/ch341.c
-static int32_t ch341_set_handshake ( cdch_interface_t* p_cdc, uint8_t control, tuh_xfer_cb_t complete_cb, uintptr_t user_data )
-{
-  return ch341_control_out ( p_cdc, CH341_REQ_MODEM_CTRL, /* value */ ~control, /* index */ 0, complete_cb, user_data );
-}
-
-// detect quirks (special versions of CH34x)
-// Parts of this functions have been taken over from Linux driver /drivers/usb/serial/ch341.c
-static int32_t ch341_detect_quirks ( tuh_xfer_t* xfer, cdch_interface_t* p_cdc, uint8_t step, uint8_t *buffer, uint16_t buffersize, tuh_xfer_cb_t complete_cb, uintptr_t user_data )
-{
-  /*
-   * A subset of CH34x devices does not support all features. The
-   * prescaler is limited and there is no support for sending a RS232
-   * break condition. A read failure when trying to set up the latter is
-   * used to detect these devices.
-   */
-  switch ( step )
+  for ( uint8_t i=0; i<CFG_TUH_CDC; i++ )
   {
-  case 1:
-    p_cdc->ch34x.quirks = 0;
-    return ch341_read_reg_request ( p_cdc, CH341_REG_BREAK, buffer, buffersize, complete_cb, user_data );
-    break;
-  case 2:
-    if ( xfer->result != XFER_RESULT_SUCCESS )
-      p_cdc->ch34x.quirks |= CH341_QUIRK_LIMITED_PRESCALER | CH341_QUIRK_SIMULATE_BREAK;
-    return true;
-    break;
-  default:
-    TU_ASSERT ( false ); // suspicious step
-    break;
+    const cdch_interface_t* p_cdc = &cdch_data[i];
+    if ( p_cdc->daddr == xfer->daddr ) return p_cdc->bInterfaceNumber;
   }
+
+  return INTERFACE_INVALID_NUMBER;
 }
 
 // internal control complete to update state such as line state, encoding
-// CH34x needs a special interface recovery due to abnormal wIndex usage
-static void ch34x_control_complete(tuh_xfer_t* xfer)
+static void ch34x_control_complete ( tuh_xfer_t* xfer )
 {
-  uint8_t const     itf_num = (uint8_t)( ( xfer->user_data & 0xff00 ) >> 8 );
+  uint8_t const     itf_num = ch34x_xfer_get_itf_num ( xfer );
   uint8_t const     idx     = tuh_cdc_itf_get_index ( xfer->daddr, itf_num );
   cdch_interface_t  *p_cdc  = get_itf ( idx );
+  uint16_t          value   = tu_le16toh ( xfer->setup->wValue );
   TU_ASSERT ( p_cdc, );
   TU_ASSERT ( p_cdc->serial_drid == SERIAL_DRIVER_CH34X, ); // ch34x_control_complete is only used for CH34x
 
-  if (xfer->result == XFER_RESULT_SUCCESS) {
-    switch (xfer->setup->bRequest) {
-    case CH341_REQ_WRITE_REG: { // register write request
-      switch ( tu_le16toh ( xfer->setup->wValue ) ) {
-      case ( CH341_REG_DIVISOR << 8 | CH341_REG_PRESCALER ): { // baudrate write
-        p_cdc->line_coding.bit_rate = p_cdc->ch34x.baud_rate;
+  if ( xfer->result == XFER_RESULT_SUCCESS ) {
+    switch ( xfer->setup->bRequest ) {
+    case CH34X_REQ_WRITE_REG: { // register write request
+      switch ( value ) {
+      case ( 0x1312 ): { // baudrate write
+        p_cdc->line_coding.bit_rate = p_cdc->baudrate_requested;
+        #if CH34X_LOGS
+          TU_LOG_BAUDRATE("CDCh CH34x Control Complete ", p_cdc->line_coding.bit_rate);
+        #endif
         break;
       }
       default: {
@@ -1512,6 +1401,20 @@ static void ch34x_control_complete(tuh_xfer_t* xfer)
       }
       break;
     }
+    case CH34X_REQ_MODEM_CTRL: { // set modem controls RTS/DTR request
+      if ( ~value & CH34X_BIT_RTS )
+        p_cdc->line_state |= CDC_CONTROL_LINE_STATE_RTS;
+      else
+        p_cdc->line_state &= ~CDC_CONTROL_LINE_STATE_RTS;
+      if ( ~value & CH34X_BIT_DTR )
+        p_cdc->line_state |= CDC_CONTROL_LINE_STATE_DTR;
+      else
+        p_cdc->line_state &= ~CDC_CONTROL_LINE_STATE_DTR;
+      #if CH34X_LOGS
+        TU_LOG_CONTROL_LINE_STATE("CDCh CH34x Control Complete ", p_cdc->line_state);
+      #endif
+      break;
+    }
     default: {
       TU_ASSERT(false, ); // unexpected request
       break;
@@ -1523,118 +1426,161 @@ static void ch34x_control_complete(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 ) // do not confuse with ch341_set_baudrate
+static bool ch34x_get_factor_divisor ( uint32_t baval, uint8_t *factor, uint8_t *divisor )
+{ // calc baudrate factor and divisor
+  uint8_t  a;
+  uint8_t  b;
+  uint32_t c;
+
+  switch (baval) {
+  case 921600:
+    a = 0xf3;
+    b = 7;
+    break;
+  case 307200:
+    a = 0xd9;
+    b = 7;
+    break;
+  default:
+    if (baval > 6000000 / 255) {
+      b = 3;
+      c = 6000000;
+    } else if (baval > 750000 / 255) {
+      b = 2;
+      c = 750000;
+    } else if (baval > 93750 / 255) {
+      b = 1;
+      c = 93750;
+    } else {
+      b = 0;
+      c = 11719;
+    }
+    a = (unsigned char)(c / baval);
+    if (a == 0 || a == 0xFF)
+      return false;
+    if ((c / a - baval) > (baval - c / (a + 1)))
+      a++;
+    a = 256 - a;
+    break;
+  }
+  *factor = a;
+  *divisor = b;
+
+  return true;
+}
+
+static bool ch34x_set_line_coding(cdch_interface_t* p_cdc, cdc_line_coding_t const* line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data)
 {
-  TU_LOG_DRV("CDC CH34x Set BaudRate = %lu\r\n", baudrate);
-  uint32_t baud_le = tu_htole32(baudrate);
-  p_cdc->ch34x.baud_rate = baudrate;
+  TU_ASSERT(false, false); // TODO
+  return false;
+}
+
+static bool ch34x_set_baudrate ( cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data )
+{
+  p_cdc->baudrate_requested = baudrate;
   p_cdc->user_control_cb = complete_cb;
-  return ch341_set_baudrate ( p_cdc, baud_le, complete_cb ? ch34x_control_complete : NULL, user_data );
+  uint8_t factor, divisor;
+  TU_ASSERT ( ch34x_get_factor_divisor ( baudrate, &factor, &divisor ), false );
+  TU_ASSERT ( ch34x_write_reg ( p_cdc, /* reg */ 0x1312, /* value */ (uint16_t)factor << 8 | 0x80 | divisor,
+                                complete_cb ? ch34x_control_complete : NULL, user_data ), false );
+
+  return true;
 }
 
 static bool ch34x_set_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 CH34x Set Control Line State\r\n");
-  // todo later
-  return false;
+  p_cdc->user_control_cb = complete_cb;
+  uint16_t control = 0;
+  if ( line_state & CDC_CONTROL_LINE_STATE_RTS )
+    control |= CH34X_BIT_RTS;
+  if ( line_state & CDC_CONTROL_LINE_STATE_DTR )
+    control |= CH34X_BIT_DTR;
+  TU_ASSERT ( ch34x_control_out ( p_cdc, /* request */ CH34X_REQ_MODEM_CTRL, /* value */ (uint8_t)~control,
+                                  /* index */ 0, complete_cb ? ch34x_control_complete : NULL, user_data ), false );
+  return true;
 }
 
 static void ch34x_process_config ( tuh_xfer_t* xfer )
 {
-  uintptr_t const   state   = xfer->user_data & 0xff;
-  // CH34x needs a special handling of bInterfaceNumber, because wIndex is used for other purposes and not for bInterfaceNumber
-  uint8_t const     itf_num = (uint8_t)( ( xfer->user_data & 0xff00 ) >> 8 );
-  uint8_t const     idx     = tuh_cdc_itf_get_index ( xfer->daddr, itf_num );
-  cdch_interface_t  *p_cdc  = get_itf ( idx );
-  uint8_t           buffer [ CH34X_BUFFER_SIZE ];
-  cdc_line_coding_t line_coding = CFG_TUH_CDC_LINE_CODING_ON_ENUM;
+  uint8_t const           itf_num     = ch34x_xfer_get_itf_num ( xfer );
+  uintptr_t const         state       = xfer->user_data;
+  uint8_t const           idx         = tuh_cdc_itf_get_index ( xfer->daddr, itf_num );
+  cdch_interface_t        *p_cdc      = get_itf ( idx );
+  cdc_line_coding_t const line_coding = CFG_TUH_CDC_LINE_CODING_ON_ENUM_CH34X;
+  uint8_t                 buffer[2];
   TU_ASSERT ( p_cdc, );
 
-  if ( state == 0 ) {
-    // defaults
-    p_cdc->ch34x.baud_rate = DEFAULT_BAUD_RATE;
-    p_cdc->ch34x.mcr = 0;
-    p_cdc->ch34x.msr = 0;
-    p_cdc->ch34x.quirks = 0;
-    p_cdc->ch34x.version = 0;
-    /*
-     * Some CH340 devices appear unable to change the initial LCR
-     * settings, so set a sane 8N1 default.
-     */
-    p_cdc->ch34x.lcr = CH341_LCR_ENABLE_RX | CH341_LCR_ENABLE_TX | CH341_LCR_CS8;
-  }
-  // This process flow has been taken over from Linux driver /drivers/usb/serial/ch341.c
   switch ( state ) {
-  case CONFIG_CH34X_STEP1: // request version read
-    TU_ASSERT ( ch341_control_in ( p_cdc, /* request */ CH341_REQ_READ_VERSION, /* value */ 0, /* index */ 0, buffer, CH34X_BUFFER_SIZE, ch34x_process_config, CONFIG_CH34X_STEP2 ), );
+  case CONFIG_CH34X_READ_VERSION: // request version read
+    #if CH34X_LOGS
+      TU_LOG_DRV ( "[%u] CDCh CH34x Process Config started\r\n", p_cdc->daddr );
+    #endif
+    TU_ASSERT ( ch34x_control_in ( p_cdc, /* request */ CH34X_REQ_READ_VERSION, /* value */ 0,
+                                   /* index */ 0, buffer, 2, ch34x_process_config, CONFIG_CH34X_SERIAL_INIT ), );
     break;
-  case CONFIG_CH34X_STEP2: // handle version read data, request to init CH34x
-    p_cdc->ch34x.version = xfer->buffer[0];
-    TU_LOG_DRV ( "Chip version=%02x\r\n", p_cdc->ch34x.version );
-    TU_ASSERT ( ch341_control_out ( p_cdc, /* request */ CH341_REQ_SERIAL_INIT, /* value */ 0, /* index */ 0, ch34x_process_config, CONFIG_CH34X_STEP3 ), );
-    break;
-  case CONFIG_CH34X_STEP3: // set baudrate with default values (see above)
-    TU_ASSERT ( ch341_set_baudrate ( p_cdc, p_cdc->ch34x.baud_rate, ch34x_process_config, CONFIG_CH34X_STEP4 ) > 0, );
-    break;
-  case CONFIG_CH34X_STEP4: // set line controls with default values (see above)
-    TU_ASSERT ( ch341_set_lcr ( p_cdc, p_cdc->ch34x.lcr, ch34x_process_config, CONFIG_CH34X_STEP5 ) > 0, );
-    break;
-  case CONFIG_CH34X_STEP5: // set handshake RTS/DTR
-    TU_ASSERT ( ch341_set_handshake ( p_cdc, p_cdc->ch34x.mcr, ch34x_process_config, CONFIG_CH34X_STEP6 ) > 0, );
-    break;
-  case CONFIG_CH34X_STEP6: // detect quirks step 1
-    TU_ASSERT ( ch341_detect_quirks ( xfer, p_cdc, /* step */ 1, buffer, CH34X_BUFFER_SIZE, ch34x_process_config, CONFIG_CH34X_STEP7 ) > 0, );
-    break;
-  case CONFIG_CH34X_STEP7: // detect quirks step 2 and set baudrate with configured values
-    TU_ASSERT ( ch341_detect_quirks ( xfer, p_cdc, /* step */ 2, NULL, 0, NULL, 0 ) > 0, );
-#ifdef CFG_TUH_CDC_LINE_CODING_ON_ENUM
-    TU_ASSERT ( ch34x_set_baudrate ( p_cdc, line_coding.bit_rate, ch34x_process_config, CONFIG_CH34X_STEP8 ), );
-#else
-    TU_ATTR_FALLTHROUGH;
-#endif
-    break;
-  case CONFIG_CH34X_STEP8: // set data/stop bit quantities, parity
-#ifdef CFG_TUH_CDC_LINE_CODING_ON_ENUM
-    p_cdc->ch34x.lcr = CH341_LCR_ENABLE_RX | CH341_LCR_ENABLE_TX;
+  case CONFIG_CH34X_SERIAL_INIT: { // handle version read data, request to init CH34x with line_coding and baudrate
+    uint8_t version = xfer->buffer[0];
+    #if CH34X_LOGS
+      TU_LOG_DRV ( "[%u] CDCh CH34x Chip version=%02x\r\n", p_cdc->daddr, version );
+    #endif
+    // only versions >= 0x30 are tested, below 0x30 seems having other programming, see WCH vendor, linux kernel and FreeBSD drivers
+    TU_ASSERT ( version >= 0x30, );
+    uint8_t factor, divisor;
+    TU_ASSERT ( ch34x_get_factor_divisor ( line_coding.bit_rate, &factor, &divisor ), );
+    uint8_t lcr = CH34X_LCR_ENABLE_RX | CH34X_LCR_ENABLE_TX;
     switch ( line_coding.data_bits ) {
     case 5:
-      p_cdc->ch34x.lcr |= CH341_LCR_CS5;
+      lcr |= CH34X_LCR_CS5;
       break;
     case 6:
-      p_cdc->ch34x.lcr |= CH341_LCR_CS6;
+      lcr |= CH34X_LCR_CS6;
       break;
     case 7:
-      p_cdc->ch34x.lcr |= CH341_LCR_CS7;
+      lcr |= CH34X_LCR_CS7;
       break;
     case 8:
-      p_cdc->ch34x.lcr |= CH341_LCR_CS8;
+      lcr |= CH34X_LCR_CS8;
       break;
     default:
       TU_ASSERT ( false, ); // not supported data_bits
-      p_cdc->ch34x.lcr |= CH341_LCR_CS8;
+      lcr |= CH34X_LCR_CS8;
       break;
     }
     if ( line_coding.parity != CDC_LINE_CODING_PARITY_NONE ) {
-      p_cdc->ch34x.lcr |= CH341_LCR_ENABLE_PAR;
+      lcr |= CH34X_LCR_ENABLE_PAR;
       if ( line_coding.parity == CDC_LINE_CODING_PARITY_EVEN || line_coding.parity == CDC_LINE_CODING_PARITY_SPACE )
-        p_cdc->ch34x.lcr |= CH341_LCR_PAR_EVEN;
+        lcr |= CH34X_LCR_PAR_EVEN;
       if ( line_coding.parity == CDC_LINE_CODING_PARITY_MARK || line_coding.parity == CDC_LINE_CODING_PARITY_SPACE )
-        p_cdc->ch34x.lcr |= CH341_LCR_MARK_SPACE;
+        lcr |= CH34X_LCR_MARK_SPACE;
     }
     TU_ASSERT ( line_coding.stop_bits == CDC_LINE_CODING_STOP_BITS_1 || line_coding.stop_bits == CDC_LINE_CODING_STOP_BITS_2, ); // not supported 1.5 stop bits
     if ( line_coding.stop_bits == CDC_LINE_CODING_STOP_BITS_2 )
-      p_cdc->ch34x.lcr |= CH341_LCR_STOP_BITS_2;
-    TU_ASSERT ( ch341_set_lcr ( p_cdc, p_cdc->ch34x.lcr, ch34x_process_config, CONFIG_CH34X_COMPLETE ) > 0, );
-#else
-    TU_ATTR_FALLTHROUGH;
-#endif
+      lcr |= CH34X_LCR_STOP_BITS_2;
+    TU_ASSERT ( ch34x_control_out ( p_cdc, /* request */ CH34X_REQ_SERIAL_INIT, /* value */ (uint16_t)lcr << 8 | 0x9c,
+                                    /* index */ (uint16_t)factor << 8 | 0x80 | divisor, ch34x_process_config, CONFIG_CH34X_SPECIAL_REG_WRITE ), );
+    break;
+  }
+  case CONFIG_CH34X_SPECIAL_REG_WRITE: // do special reg write, purpose unknown, overtaken from WCH driver
+    TU_ASSERT ( ch34x_write_reg ( p_cdc, /* reg */ 0x0f2c, /* value */ 0x0007, ch34x_process_config, CONFIG_CH34X_FLOW_CONTROL ), );
+    break;
+  case CONFIG_CH34X_FLOW_CONTROL: // no hardware flow control
+    TU_ASSERT ( ch34x_write_reg ( p_cdc, /* reg */ 0x2727, /* value */ 0x0000, ch34x_process_config, CONFIG_CH34X_MODEM_CONTROL ), );
+    break;
+  case CONFIG_CH34X_MODEM_CONTROL: // !always! set modem controls RTS/DTR (CH34x has no reset state after CH34X_REQ_SERIAL_INIT)
+    TU_ASSERT ( ch34x_set_modem_ctrl ( p_cdc, CFG_TUH_CDC_LINE_CONTROL_ON_ENUM, ch34x_process_config, CONFIG_CH34X_COMPLETE ), );
+    break;
+  case CONFIG_CH34X_COMPLETE:
+    p_cdc->line_coding = line_coding; // CONFIG_CH34X_SERIAL_INIT not handled by ch34x_control_complete
+    #if CH34X_LOGS
+      TU_LOG_DRV("CDCh CH34x Process Config Complete\r\n");
+      TU_LOG_LINE_CODING("  ", p_cdc->line_coding);
+      TU_LOG_CONTROL_LINE_STATE("  ", p_cdc->line_state);
+    #endif
+    set_config_complete ( p_cdc, idx, itf_num );
+    break;
+  default:
+    TU_ASSERT ( false, );
     break;
-    case CONFIG_CH34X_COMPLETE:
-      set_config_complete ( p_cdc, idx, itf_num );
-      break;
-    default:
-      TU_ASSERT ( false, );
-      break;
   }
 }
 
diff --git a/src/class/cdc/cdc_host.h b/src/class/cdc/cdc_host.h
index 9e5edd94e..8e3a2352a 100644
--- a/src/class/cdc/cdc_host.h
+++ b/src/class/cdc/cdc_host.h
@@ -37,7 +37,7 @@
 // Class Driver Configuration
 //--------------------------------------------------------------------+
 
-// Set Line Control state on enumeration/mounted: DTR ( bit 0), RTS (bit 1)
+// Set Line Control state on enumeration/mounted, refer enums CDC_CONTROL_LINE_STATE_RTS, CDC_CONTROL_LINE_STATE_DTR
 #ifndef CFG_TUH_CDC_LINE_CONTROL_ON_ENUM
 #define CFG_TUH_CDC_LINE_CONTROL_ON_ENUM    0
 #endif
@@ -142,7 +142,7 @@ bool tuh_cdc_read_clear (uint8_t idx);
 //   - The function will return true if transfer is successful, false otherwise.
 //--------------------------------------------------------------------+
 
-// Request to Set Control Line State: DTR (bit 0), RTS (bit 1)
+// Request to Set Control Line State, refer enums CDC_CONTROL_LINE_STATE_RTS, CDC_CONTROL_LINE_STATE_DTR
 bool tuh_cdc_set_control_line_state(uint8_t idx, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
 
 // Request to set baudrate
diff --git a/src/class/cdc/serial/ch34x.h b/src/class/cdc/serial/ch34x.h
index 94e18b252..262e012fd 100644
--- a/src/class/cdc/serial/ch34x.h
+++ b/src/class/cdc/serial/ch34x.h
@@ -1,7 +1,7 @@
 /*
  * The MIT License (MIT)
  *
- * Copyright (c) 2023 Heiko Kuester (tinyusb.org)
+ * Copyright (c) 2023 IngHK Heiko Kuester (tinyusb.org)
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * of this software and associated documentation files (the "Software"), to deal
@@ -27,87 +27,34 @@
 #ifndef _CH34X_H_
 #define _CH34X_H_
 
-#include <stdint.h>
-
-#define BIT(nr) ( (uint32_t)1 << (nr) )
-
-#define CH34X_BUFFER_SIZE 2
-
-// The following defines have been taken over from Linux driver /drivers/usb/serial/ch341.c
-
-#define DEFAULT_BAUD_RATE 9600
-
-/* flags for IO-Bits */
-#define CH341_BIT_RTS (1 << 6)
-#define CH341_BIT_DTR (1 << 5)
-
-/******************************/
-/* interrupt pipe definitions */
-/******************************/
-/* always 4 interrupt bytes */
-/* first irq byte normally 0x08 */
-/* second irq byte base 0x7d + below */
-/* third irq byte base 0x94 + below */
-/* fourth irq byte normally 0xee */
-
-/* second interrupt byte */
-#define CH341_MULT_STAT 0x04 /* multiple status since last interrupt event */
-
-/* status returned in third interrupt answer byte, inverted in data
-   from irq */
-#define CH341_BIT_CTS 0x01
-#define CH341_BIT_DSR 0x02
-#define CH341_BIT_RI  0x04
-#define CH341_BIT_DCD 0x08
-#define CH341_BITS_MODEM_STAT 0x0f /* all bits */
-
-/* Break support - the information used to implement this was gleaned from
- * the Net/FreeBSD uchcom.c driver by Takanori Watanabe.  Domo arigato.
- */
+// 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 CH341_REQ_READ_VERSION 0x5F // dec 95
-#define CH341_REQ_WRITE_REG    0x9A
-#define CH341_REQ_READ_REG     0x95
-#define CH341_REQ_SERIAL_INIT  0xA1
-#define CH341_REQ_MODEM_CTRL   0xA4
+#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
 
-// CH34x registers
-#define CH341_REG_BREAK        0x05
-#define CH341_REG_PRESCALER    0x12
-#define CH341_REG_DIVISOR      0x13
-#define CH341_REG_LCR          0x18
-#define CH341_REG_LCR2         0x25
-
-#define CH341_NBREAK_BITS      0x01
+// modem control bits
+#define CH34X_BIT_RTS ( 1 << 6 )
+#define CH34X_BIT_DTR ( 1 << 5 )
 
 // line control bits
-#define CH341_LCR_ENABLE_RX    0x80
-#define CH341_LCR_ENABLE_TX    0x40
-#define CH341_LCR_MARK_SPACE   0x20
-#define CH341_LCR_PAR_EVEN     0x10
-#define CH341_LCR_ENABLE_PAR   0x08
-#define CH341_LCR_STOP_BITS_2  0x04
-#define CH341_LCR_CS8          0x03
-#define CH341_LCR_CS7          0x02
-#define CH341_LCR_CS6          0x01
-#define CH341_LCR_CS5          0x00
-
-#define CH341_QUIRK_LIMITED_PRESCALER BIT(0)
-#define CH341_QUIRK_SIMULATE_BREAK  BIT(1)
-
-#define CH341_CLKRATE   48000000
-#define CH341_CLK_DIV(ps, fact) (1 << (12 - 3 * (ps) - (fact)))
-#define CH341_MIN_RATE(ps)  (CH341_CLKRATE / (CH341_CLK_DIV((ps), 1) * 512))
-
-/* Supported range is 46 to 3000000 bps. */
-#define CH341_MIN_BPS DIV_ROUND_UP(CH341_CLKRATE, CH341_CLK_DIV(0, 0) * 256)
-#define CH341_MAX_BPS (CH341_CLKRATE / (CH341_CLK_DIV(3, 0) * 2))
-
-#define DIV_ROUND_UP __KERNEL_DIV_ROUND_UP
-#define __KERNEL_DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d))
-
-// error codes
-#define EINVAL    22  /* Invalid argument */
+#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_STOP_BITS_2  0x04
+#define CH34X_LCR_CS8          0x03
+#define CH34X_LCR_CS7          0x02
+#define CH34X_LCR_CS6          0x01
+#define CH34X_LCR_CS5          0x00
 
 #endif /* _CH34X_H_ */