summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsakumisu <[email protected]>2025-12-12 22:45:10 +0800
committersakumisu <[email protected]>2025-12-12 22:45:10 +0800
commitda2263728aba07a28d9b6946b7acb6b0a0a97bb1 (patch)
tree2a53759e9992b54dbf582b8ba111e0c54e1bcf1d
parent707e865627e65b9d661a431c92d57e50ab9d20ab (diff)
refactor(serial): add host serial framework
Signed-off-by: sakumisu <[email protected]>
-rw-r--r--CMakeLists.txt5
-rw-r--r--Kconfig25
-rw-r--r--Kconfig.rtt25
-rw-r--r--Kconfig.rttpkg24
-rw-r--r--README.md5
-rw-r--r--README_zh.md5
-rw-r--r--SConscript8
-rw-r--r--cherryusb.cmake27
-rw-r--r--cherryusb_config_template.h6
-rw-r--r--class/cdc/usbh_cdc_acm.c294
-rw-r--r--class/cdc/usbh_cdc_acm.h50
-rw-r--r--class/serial/usbh_cdc_acm.c266
-rw-r--r--class/serial/usbh_cdc_acm.h19
-rw-r--r--class/serial/usbh_ch34x.c410
-rw-r--r--class/serial/usbh_ch34x.h56
-rw-r--r--class/serial/usbh_cp210x.c510
-rw-r--r--class/serial/usbh_cp210x.h187
-rw-r--r--class/serial/usbh_ftdi.c407
-rw-r--r--class/serial/usbh_ftdi.h341
-rw-r--r--class/serial/usbh_gsm.c127
-rw-r--r--class/serial/usbh_pl2303.c726
-rw-r--r--class/serial/usbh_pl2303.h43
-rw-r--r--class/serial/usbh_serial.c719
-rw-r--r--class/serial/usbh_serial.h179
-rw-r--r--class/vendor/serial/usbh_ch34x.c379
-rw-r--r--class/vendor/serial/usbh_ch34x.h76
-rw-r--r--class/vendor/serial/usbh_cp210x.c328
-rw-r--r--class/vendor/serial/usbh_cp210x.h73
-rw-r--r--class/vendor/serial/usbh_ftdi.c510
-rw-r--r--class/vendor/serial/usbh_ftdi.h96
-rw-r--r--class/vendor/serial/usbh_pl2303.c449
-rw-r--r--class/vendor/serial/usbh_pl2303.h62
-rw-r--r--common/usb_util.h17
-rw-r--r--demo/usb_host.c149
-rw-r--r--docs/source/api/api_host.rst105
-rw-r--r--docs/source/demo/usbh_serial.rst94
-rw-r--r--osal/idf/usb_config.h8
-rw-r--r--platform/rtthread/usb_msh.c6
-rw-r--r--platform/rtthread/usbh_rtserial.c209
-rw-r--r--platform/rtthread/usbh_serial.c899
-rw-r--r--tests/bouffalolab/inc/usb_config.h6
-rw-r--r--tests/hpmicro/inc/usb_config.h8
-rw-r--r--tests/hpmicro/src/main.c2
43 files changed, 4646 insertions, 3294 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 5c3e651b..668f75ec 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -26,6 +26,7 @@ if(BL_SDK_BASE)
set(CONFIG_CHERRYUSB_HOST_CP210X 1)
set(CONFIG_CHERRYUSB_HOST_FTDI 1)
set(CONFIG_CHERRYUSB_HOST_PL2303 1)
+ set(CONFIG_CHERRYUSB_HOST_GSM 1)
set(CONFIG_CHERRYUSB_DEVICE_BL 1)
set(CONFIG_CHERRYUSB_HOST_EHCI_BL 1)
@@ -139,6 +140,9 @@ elseif(ESP_PLATFORM)
if(CONFIG_CHERRYUSB_HOST_PL2303)
target_link_libraries(${COMPONENT_LIB} INTERFACE "-u pl2303_class_info")
endif()
+ if(CONFIG_CHERRYUSB_HOST_GSM)
+ target_link_libraries(${COMPONENT_LIB} INTERFACE "-u gsm_class_info")
+ endif()
endif()
if(CONFIG_CHERRYUSB)
@@ -194,6 +198,7 @@ elseif(HPM_SDK_BASE)
set(CONFIG_CHERRYUSB_HOST_CP210X 1)
set(CONFIG_CHERRYUSB_HOST_FTDI 1)
set(CONFIG_CHERRYUSB_HOST_PL2303 1)
+ set(CONFIG_CHERRYUSB_HOST_GSM 1)
set(CONFIG_CHERRYUSB_DEVICE_HPM 1)
set(CONFIG_CHERRYUSB_HOST_EHCI_HPM 1)
diff --git a/Kconfig b/Kconfig
index 812a5585..f4c8128c 100644
--- a/Kconfig
+++ b/Kconfig
@@ -299,6 +299,7 @@ if CHERRYUSB
config CHERRYUSB_HOST_CDC_ACM
bool
prompt "Enable usb cdc acm driver"
+ select USBHOST_SERIAL
default n
config CHERRYUSB_HOST_HID
@@ -360,21 +361,31 @@ if CHERRYUSB
config CHERRYUSB_HOST_FTDI
bool
prompt "Enable usb ftdi driver"
+ select USBHOST_SERIAL
default n
config CHERRYUSB_HOST_CH34X
bool
prompt "Enable usb ch34x driver"
+ select USBHOST_SERIAL
default n
config CHERRYUSB_HOST_CP210X
bool
prompt "Enable usb cp210x driver"
+ select USBHOST_SERIAL
default n
config CHERRYUSB_HOST_PL2303
bool
prompt "Enable usb pl2303 driver"
+ select USBHOST_SERIAL
+ default n
+
+ config CHERRYUSB_HOST_GSM
+ bool
+ prompt "Enable usb gsm driver for 4g module"
+ select USBHOST_SERIAL
default n
config CHERRYUSB_HOST_AOA
@@ -382,6 +393,9 @@ if CHERRYUSB
prompt "Enable usb aoa driver"
default n
+ config USBHOST_SERIAL
+ bool
+
config USBHOST_PLATFORM_CDC_ECM
bool
@@ -417,12 +431,17 @@ if CHERRYUSB
prompt "Set host control transfer timeout, unit is ms"
default 500
+ config USBHOST_SERIAL_RX_SIZE
+ int
+ prompt "Set host serial rx max buffer size"
+ default 2048
+
menu "Select USB host template, please select class driver first"
- config TEST_USBH_CDC_ACM
+ config TEST_USBH_SERIAL
int
- prompt "demo for test cdc acm"
+ prompt "demo for test serial"
default 0
- depends on CHERRYUSB_HOST_CDC_ACM
+ depends on CHERRYUSB_HOST_CDC_ACM || CHERRYUSB_HOST_FTDI || CHERRYUSB_HOST_CH34X || CHERRYUSB_HOST_CP210X || CHERRYUSB_HOST_PL2303
config TEST_USBH_HID
int
prompt "demo for test hid"
diff --git a/Kconfig.rtt b/Kconfig.rtt
index 89cfbbfc..38747af6 100644
--- a/Kconfig.rtt
+++ b/Kconfig.rtt
@@ -315,6 +315,7 @@ if RT_USING_CHERRYUSB
config RT_CHERRYUSB_HOST_CDC_ACM
bool
prompt "Enable usb cdc acm driver"
+ select CONFIG_USBHOST_SERIAL
default n
config RT_CHERRYUSB_HOST_HID
@@ -382,23 +383,36 @@ if RT_USING_CHERRYUSB
config RT_CHERRYUSB_HOST_FTDI
bool
prompt "Enable usb ftdi driver"
+ select CONFIG_USBHOST_SERIAL
default n
config RT_CHERRYUSB_HOST_CH34X
bool
prompt "Enable usb ch34x driver"
+ select CONFIG_USBHOST_SERIAL
default n
config RT_CHERRYUSB_HOST_CP210X
bool
prompt "Enable usb cp210x driver"
+ select CONFIG_USBHOST_SERIAL
default n
config RT_CHERRYUSB_HOST_PL2303
bool
prompt "Enable usb pl2303 driver"
+ select CONFIG_USBHOST_SERIAL
default n
+ config RT_CHERRYUSB_HOST_GSM
+ bool
+ prompt "Enable usb gsm driver for 4g module"
+ select CONFIG_USBHOST_SERIAL
+ default n
+
+ config CONFIG_USBHOST_SERIAL
+ bool
+
config CONFIG_USBHOST_PLATFORM_CDC_ECM
bool
@@ -434,6 +448,11 @@ if RT_USING_CHERRYUSB
prompt "Set host control transfer timeout, unit is ms"
default 500
+ config CONFIG_USBHOST_SERIAL_RX_SIZE
+ int
+ prompt "Set host serial rx max buffer size"
+ default 2048
+
config RT_LWIP_PBUF_POOL_BUFSIZE
int "The size of each pbuf in the pbuf pool"
range 1500 2000
@@ -445,11 +464,11 @@ if RT_USING_CHERRYUSB
default "/"
menu "Select USB host template, please select class driver first"
- config CONFIG_TEST_USBH_CDC_ACM
+ config CONFIG_TEST_USBH_SERIAL
int
- prompt "demo for test cdc acm, cannot enable this demo, we have used serial framework instead"
+ prompt "demo for test serial, cannot enable this demo, we have used serial framework instead"
default 0
- depends on RT_CHERRYUSB_HOST_CDC_ACM
+ depends on RT_CHERRYUSB_HOST_CDC_ACM || RT_CHERRYUSB_HOST_FTDI || RT_CHERRYUSB_HOST_CH34X || RT_CHERRYUSB_HOST_CP210X || RT_CHERRYUSB_HOST_PL2303
config CONFIG_TEST_USBH_HID
int
prompt "demo for test hid"
diff --git a/Kconfig.rttpkg b/Kconfig.rttpkg
index a48d6795..b1d10afa 100644
--- a/Kconfig.rttpkg
+++ b/Kconfig.rttpkg
@@ -381,23 +381,36 @@ if PKG_USING_CHERRYUSB
config PKG_CHERRYUSB_HOST_FTDI
bool
prompt "Enable usb ftdi driver"
+ select CONFIG_USBHOST_SERIAL
default n
config PKG_CHERRYUSB_HOST_CH34X
bool
prompt "Enable usb ch34x driver"
+ select CONFIG_USBHOST_SERIAL
default n
config PKG_CHERRYUSB_HOST_CP210X
bool
prompt "Enable usb cp210x driver"
+ select CONFIG_USBHOST_SERIAL
default n
config PKG_CHERRYUSB_HOST_PL2303
bool
prompt "Enable usb pl2303 driver"
+ select CONFIG_USBHOST_SERIAL
default n
+ config PKG_CHERRYUSB_HOST_GSM
+ bool
+ prompt "Enable usb gsm driver for 4g module"
+ select CONFIG_USBHOST_SERIAL
+ default n
+
+ config CONFIG_USBHOST_SERIAL
+ bool
+
config CONFIG_USBHOST_PLATFORM_CDC_ECM
bool
@@ -433,6 +446,11 @@ if PKG_USING_CHERRYUSB
prompt "Set host control transfer timeout, unit is ms"
default 500
+ config CONFIG_USBHOST_SERIAL_RX_SIZE
+ int
+ prompt "Set host serial rx max buffer size"
+ default 2048
+
config RT_LWIP_PBUF_POOL_BUFSIZE
int "The size of each pbuf in the pbuf pool"
range 1500 2000
@@ -444,11 +462,11 @@ if PKG_USING_CHERRYUSB
default "/"
menu "Select USB host template, please select class driver first"
- config CONFIG_TEST_USBH_CDC_ACM
+ config CONFIG_TEST_USBH_SERIAL
int
- prompt "demo for test cdc acm, cannot enable this demo, we have used serial framework instead"
+ prompt "demo for test usb serial, cannot enable this demo, we have used serial framework instead"
default 0
- depends on PKG_CHERRYUSB_HOST_CDC_ACM
+ depends on PKG_CHERRYUSB_HOST_CDC_ACM || PKG_CHERRYUSB_HOST_FTDI || PKG_CHERRYUSB_HOST_CH34X || PKG_CHERRYUSB_HOST_CP210X || PKG_CHERRYUSB_HOST_PL2303
config CONFIG_TEST_USBH_HID
int
prompt "demo for test hid"
diff --git a/README.md b/README.md
index d364ed64..e74a4417 100644
--- a/README.md
+++ b/README.md
@@ -112,7 +112,8 @@ CherryUSB Host Stack has the following functions:
- Support USB Audio CLASS (UAC1.0)
- Support Remote NDIS (RNDIS)
- Support USB Bluetooth class (support nimble and zephyr bluetooth stack, support **CLASS:0xE0** or vendor class like cdc acm)
-- Support Vendor class (serial, net, wifi)
+- Support Vendor Serial Class(CH34X、CP210X、PL2303、FTDI、GSM)
+- Support Vendor network Class(RTL8152、AX88772)
- Support USB modeswitch
- Support Android Open Accessory
- Support multi host with the same USB IP
@@ -150,7 +151,7 @@ Among them, `sizeof(struct usbh_hub)` and `sizeof(struct usbh_hubport)` are affe
x is affected by the following macros:
```
-#define CONFIG_USBHOST_MAX_CDC_ACM_CLASS 4
+#define CONFIG_USBHOST_MAX_SERIAL_CLASS 4
#define CONFIG_USBHOST_MAX_HID_CLASS 4
#define CONFIG_USBHOST_MAX_MSC_CLASS 2
#define CONFIG_USBHOST_MAX_AUDIO_CLASS 1
diff --git a/README_zh.md b/README_zh.md
index 1e1ec40c..aeae3a9f 100644
--- a/README_zh.md
+++ b/README_zh.md
@@ -112,7 +112,8 @@ CherryUSB Host 协议栈当前实现以下功能:
- Support USB Audio CLASS (UAC1.0)
- 支持 Remote NDIS (RNDIS)
- 支持 USB Bluetooth (支持 nimble and zephyr bluetooth 协议栈,支持 **CLASS: 0xE0** 或者厂家自定义类,类似于 cdc acm 功能)
-- 支持 Vendor 类 class (serial, net, wifi)
+- 支持 Vendor Serial 类(CH34X、CP210X、PL2303、FTDI、GSM)
+- 支持 Vendor network 类(RTL8152、AX88772)
- 支持 USB modeswitch
- 支持 Android Open Accessory
- 支持相同 USB IP 的多主机
@@ -150,7 +151,7 @@ CherryUSB Host 协议栈资源占用说明(GCC 10.2 with -O2,关闭 log)�
x 受以下宏影响:
```
-#define CONFIG_USBHOST_MAX_CDC_ACM_CLASS 4
+#define CONFIG_USBHOST_MAX_SERIAL_CLASS 4
#define CONFIG_USBHOST_MAX_HID_CLASS 4
#define CONFIG_USBHOST_MAX_MSC_CLASS 2
#define CONFIG_USBHOST_MAX_AUDIO_CLASS 1
diff --git a/SConscript b/SConscript
index a4bc8163..9cccc961 100644
--- a/SConscript
+++ b/SConscript
@@ -14,7 +14,7 @@ path += [cwd + '/class/wireless']
path += [cwd + '/class/midi']
path += [cwd + '/class/adb']
path += [cwd + '/class/dfu']
-path += [cwd + '/class/midi']
+path += [cwd + '/class/serial']
path += [cwd + '/class/vendor/net']
path += [cwd + '/class/vendor/serial']
path += [cwd + '/class/vendor/wifi']
@@ -306,8 +306,10 @@ if GetDepend(['PKG_CHERRYUSB_HOST']):
or GetDepend(['PKG_CHERRYUSB_HOST_FTDI']) \
or GetDepend(['PKG_CHERRYUSB_HOST_CH34X']) \
or GetDepend(['PKG_CHERRYUSB_HOST_CP210X']) \
- or GetDepend(['PKG_CHERRYUSB_HOST_PL2303']):
- src += Glob('platform/rtthread/usbh_serial.c')
+ or GetDepend(['PKG_CHERRYUSB_HOST_PL2303']) \
+ or GetDepend(['PKG_CHERRYUSB_HOST_GSM']):
+ src += Glob('class/serial/usbh_serial.c')
+ src += Glob('platform/rtthread/usbh_rtserial.c')
if GetDepend('RT_USING_DFS') and GetDepend(['PKG_CHERRYUSB_HOST_MSC']):
src += Glob('platform/rtthread/usbh_dfs.c')
diff --git a/cherryusb.cmake b/cherryusb.cmake
index 540ce97f..c8aa8621 100644
--- a/cherryusb.cmake
+++ b/cherryusb.cmake
@@ -47,8 +47,8 @@ list(
${CMAKE_CURRENT_LIST_DIR}/class/midi
${CMAKE_CURRENT_LIST_DIR}/class/adb
${CMAKE_CURRENT_LIST_DIR}/class/dfu
+ ${CMAKE_CURRENT_LIST_DIR}/class/serial
${CMAKE_CURRENT_LIST_DIR}/class/vendor/net
- ${CMAKE_CURRENT_LIST_DIR}/class/vendor/serial
${CMAKE_CURRENT_LIST_DIR}/class/vendor/wifi
${CMAKE_CURRENT_LIST_DIR}/class/aoa
)
@@ -156,7 +156,7 @@ if(CONFIG_CHERRYUSB_HOST)
)
if(CONFIG_CHERRYUSB_HOST_CDC_ACM)
- list(APPEND cherryusb_srcs ${CMAKE_CURRENT_LIST_DIR}/class/cdc/usbh_cdc_acm.c)
+ list(APPEND cherryusb_srcs ${CMAKE_CURRENT_LIST_DIR}/class/serial/usbh_cdc_acm.c)
endif()
if(CONFIG_CHERRYUSB_HOST_CDC_ECM)
list(APPEND cherryusb_srcs ${CMAKE_CURRENT_LIST_DIR}/class/cdc/usbh_cdc_ecm.c)
@@ -235,21 +235,34 @@ if(CONFIG_CHERRYUSB_HOST)
list(APPEND cherryusb_srcs ${CMAKE_CURRENT_LIST_DIR}/class/vendor/net/usbh_rtl8152.c)
endif()
if(CONFIG_CHERRYUSB_HOST_CH34X)
- list(APPEND cherryusb_srcs ${CMAKE_CURRENT_LIST_DIR}/class/vendor/serial/usbh_ch34x.c)
+ list(APPEND cherryusb_srcs ${CMAKE_CURRENT_LIST_DIR}/class/serial/usbh_ch34x.c)
endif()
if(CONFIG_CHERRYUSB_HOST_CP210X)
- list(APPEND cherryusb_srcs ${CMAKE_CURRENT_LIST_DIR}/class/vendor/serial/usbh_cp210x.c)
+ list(APPEND cherryusb_srcs ${CMAKE_CURRENT_LIST_DIR}/class/serial/usbh_cp210x.c)
endif()
if(CONFIG_CHERRYUSB_HOST_FTDI)
- list(APPEND cherryusb_srcs ${CMAKE_CURRENT_LIST_DIR}/class/vendor/serial/usbh_ftdi.c)
+ list(APPEND cherryusb_srcs ${CMAKE_CURRENT_LIST_DIR}/class/serial/usbh_ftdi.c)
endif()
if(CONFIG_CHERRYUSB_HOST_PL2303)
- list(APPEND cherryusb_srcs ${CMAKE_CURRENT_LIST_DIR}/class/vendor/serial/usbh_pl2303.c)
+ list(APPEND cherryusb_srcs ${CMAKE_CURRENT_LIST_DIR}/class/serial/usbh_pl2303.c)
+ endif()
+ if(CONFIG_CHERRYUSB_HOST_GSM)
+ list(APPEND cherryusb_srcs ${CMAKE_CURRENT_LIST_DIR}/class/serial/usbh_gsm.c)
endif()
if(CONFIG_CHERRYUSB_HOST_AOA)
list(APPEND cherryusb_srcs ${CMAKE_CURRENT_LIST_DIR}/class/aoa/usbh_aoa.c)
endif()
+ if(CONFIG_CHERRYUSB_HOST_CDC_ACM
+ OR CONFIG_CHERRYUSB_HOST_CH34X
+ OR CONFIG_CHERRYUSB_HOST_CP210X
+ OR CONFIG_CHERRYUSB_HOST_FTDI
+ OR CONFIG_CHERRYUSB_HOST_PL2303
+ OR CONFIG_CHERRYUSB_HOST_GSM
+ )
+ list(APPEND cherryusb_srcs ${CMAKE_CURRENT_LIST_DIR}/class/serial/usbh_serial.c)
+ endif()
+
if(CONFIG_CHERRYUSB_HOST_CDC_ECM
OR CONFIG_CHERRYUSB_HOST_CDC_RNDIS
OR CONFIG_CHERRYUSB_HOST_CDC_NCM
@@ -331,7 +344,7 @@ if(CONFIG_CHERRYUSB_HOST)
list(APPEND cherryusb_srcs ${CMAKE_CURRENT_LIST_DIR}/port/rp2040/usb_hc_rp2040.c)
endif()
- if(CONFIG_TEST_USBH_CDC_ACM OR CONFIG_TEST_USBH_HID OR CONFIG_TEST_USBH_MSC)
+ if(CONFIG_TEST_USBH_SERIAL OR CONFIG_TEST_USBH_HID OR CONFIG_TEST_USBH_MSC)
list(APPEND cherryusb_srcs ${CMAKE_CURRENT_LIST_DIR}/demo/usb_host.c)
endif()
endif()
diff --git a/cherryusb_config_template.h b/cherryusb_config_template.h
index 34c19cba..9f069005 100644
--- a/cherryusb_config_template.h
+++ b/cherryusb_config_template.h
@@ -157,7 +157,7 @@
#define CONFIG_USBHOST_MAX_INTF_ALTSETTINGS 2
#define CONFIG_USBHOST_MAX_ENDPOINTS 4
-#define CONFIG_USBHOST_MAX_CDC_ACM_CLASS 4
+#define CONFIG_USBHOST_MAX_SERIAL_CLASS 4
#define CONFIG_USBHOST_MAX_HID_CLASS 4
#define CONFIG_USBHOST_MAX_MSC_CLASS 2
#define CONFIG_USBHOST_MAX_AUDIO_CLASS 1
@@ -188,6 +188,10 @@
#define CONFIG_USBHOST_CONTROL_TRANSFER_TIMEOUT 500
#endif
+#ifndef CONFIG_USBHOST_SERIAL_RX_SIZE
+#define CONFIG_USBHOST_SERIAL_RX_SIZE 2048
+#endif
+
#ifndef CONFIG_USBHOST_MSC_TIMEOUT
#define CONFIG_USBHOST_MSC_TIMEOUT 5000
#endif
diff --git a/class/cdc/usbh_cdc_acm.c b/class/cdc/usbh_cdc_acm.c
deleted file mode 100644
index 3adf189b..00000000
--- a/class/cdc/usbh_cdc_acm.c
+++ /dev/null
@@ -1,294 +0,0 @@
-/*
- * Copyright (c) 2022, sakumisu
- *
- * SPDX-License-Identifier: Apache-2.0
- */
-#include "usbh_core.h"
-#include "usbh_cdc_acm.h"
-
-#undef USB_DBG_TAG
-#define USB_DBG_TAG "usbh_cdc_acm"
-#include "usb_log.h"
-
-#define DEV_FORMAT "/dev/ttyACM%d"
-
-USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_cdc_acm_buf[CONFIG_USBHOST_MAX_CDC_ACM_CLASS][USB_ALIGN_UP(64, CONFIG_USB_ALIGN_SIZE)];
-
-static struct usbh_cdc_acm g_cdc_acm_class[CONFIG_USBHOST_MAX_CDC_ACM_CLASS];
-static uint32_t g_devinuse = 0;
-
-static struct usbh_cdc_acm *usbh_cdc_acm_class_alloc(void)
-{
- uint8_t devno;
-
- for (devno = 0; devno < CONFIG_USBHOST_MAX_CDC_ACM_CLASS; devno++) {
- if ((g_devinuse & (1U << devno)) == 0) {
- g_devinuse |= (1U << devno);
- memset(&g_cdc_acm_class[devno], 0, sizeof(struct usbh_cdc_acm));
- g_cdc_acm_class[devno].minor = devno;
- return &g_cdc_acm_class[devno];
- }
- }
- return NULL;
-}
-
-static void usbh_cdc_acm_class_free(struct usbh_cdc_acm *cdc_acm_class)
-{
- uint8_t devno = cdc_acm_class->minor;
-
- if (devno < 32) {
- g_devinuse &= ~(1U << devno);
- }
- memset(cdc_acm_class, 0, sizeof(struct usbh_cdc_acm));
-}
-
-int usbh_cdc_acm_set_line_coding(struct usbh_cdc_acm *cdc_acm_class, struct cdc_line_coding *line_coding)
-{
- struct usb_setup_packet *setup;
-
- if (!cdc_acm_class || !cdc_acm_class->hport) {
- return -USB_ERR_INVAL;
- }
- setup = cdc_acm_class->hport->setup;
-
- setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE;
- setup->bRequest = CDC_REQUEST_SET_LINE_CODING;
- setup->wValue = 0;
- setup->wIndex = cdc_acm_class->intf;
- setup->wLength = 7;
-
- memcpy(g_cdc_acm_buf[cdc_acm_class->minor], line_coding, sizeof(struct cdc_line_coding));
-
- return usbh_control_transfer(cdc_acm_class->hport, setup, g_cdc_acm_buf[cdc_acm_class->minor]);
-}
-
-int usbh_cdc_acm_get_line_coding(struct usbh_cdc_acm *cdc_acm_class, struct cdc_line_coding *line_coding)
-{
- struct usb_setup_packet *setup;
- int ret;
-
- if (!cdc_acm_class || !cdc_acm_class->hport) {
- return -USB_ERR_INVAL;
- }
- setup = cdc_acm_class->hport->setup;
-
- setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE;
- setup->bRequest = CDC_REQUEST_GET_LINE_CODING;
- setup->wValue = 0;
- setup->wIndex = cdc_acm_class->intf;
- setup->wLength = 7;
-
- ret = usbh_control_transfer(cdc_acm_class->hport, setup, g_cdc_acm_buf[cdc_acm_class->minor]);
- if (ret < 0) {
- return ret;
- }
- memcpy(line_coding, g_cdc_acm_buf[cdc_acm_class->minor], sizeof(struct cdc_line_coding));
- return ret;
-}
-
-int usbh_cdc_acm_set_line_state(struct usbh_cdc_acm *cdc_acm_class, bool dtr, bool rts)
-{
- struct usb_setup_packet *setup;
-
- if (!cdc_acm_class || !cdc_acm_class->hport) {
- return -USB_ERR_INVAL;
- }
- setup = cdc_acm_class->hport->setup;
-
- setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE;
- setup->bRequest = CDC_REQUEST_SET_CONTROL_LINE_STATE;
- setup->wValue = (dtr << 0) | (rts << 1);
- setup->wIndex = cdc_acm_class->intf;
- setup->wLength = 0;
-
- return usbh_control_transfer(cdc_acm_class->hport, setup, NULL);
-}
-
-static int usbh_cdc_acm_connect(struct usbh_hubport *hport, uint8_t intf)
-{
- struct usb_endpoint_descriptor *ep_desc;
- int ret = 0;
-
- struct usbh_cdc_acm *cdc_acm_class = usbh_cdc_acm_class_alloc();
- if (cdc_acm_class == NULL) {
- USB_LOG_ERR("Fail to alloc cdc_acm_class\r\n");
- return -USB_ERR_NOMEM;
- }
-
- cdc_acm_class->hport = hport;
- cdc_acm_class->intf = intf;
-
- hport->config.intf[intf].priv = cdc_acm_class;
- hport->config.intf[intf + 1].priv = NULL;
-
-#ifdef CONFIG_USBHOST_CDC_ACM_NOTIFY
- ep_desc = &hport->config.intf[intf].altsetting[0].ep[0].ep_desc;
- USBH_EP_INIT(cdc_acm_class->intin, ep_desc);
-#endif
- for (uint8_t i = 0; i < hport->config.intf[intf + 1].altsetting[0].intf_desc.bNumEndpoints; i++) {
- ep_desc = &hport->config.intf[intf + 1].altsetting[0].ep[i].ep_desc;
-
- if (ep_desc->bEndpointAddress & 0x80) {
- USBH_EP_INIT(cdc_acm_class->bulkin, ep_desc);
- } else {
- USBH_EP_INIT(cdc_acm_class->bulkout, ep_desc);
- }
- }
-
- snprintf(hport->config.intf[intf].devname, CONFIG_USBHOST_DEV_NAMELEN, DEV_FORMAT, cdc_acm_class->minor);
-
- USB_LOG_INFO("Register CDC ACM Class:%s\r\n", hport->config.intf[intf].devname);
-
-#if 0
- USB_LOG_INFO("Test cdc acm rx and tx and rx for 5 times, baudrate is 115200\r\n");
-
- struct cdc_line_coding linecoding;
- uint8_t count = 5;
-
- linecoding.dwDTERate = 115200;
- linecoding.bDataBits = 8;
- linecoding.bParityType = 0;
- linecoding.bCharFormat = 0;
- usbh_cdc_acm_set_line_coding(cdc_acm_class, &linecoding);
- usbh_cdc_acm_set_line_state(cdc_acm_class, true, false);
-
- memset(g_cdc_acm_buf, 'a', sizeof(g_cdc_acm_buf));
- ret = usbh_cdc_acm_bulk_out_transfer(cdc_acm_class, g_cdc_acm_buf, sizeof(g_cdc_acm_buf), 0xfffffff);
- USB_LOG_RAW("out ret:%d\r\n", ret);
- while (count--) {
- ret = usbh_cdc_acm_bulk_in_transfer(cdc_acm_class, g_cdc_acm_buf, sizeof(g_cdc_acm_buf), 0xfffffff);
- USB_LOG_RAW("in ret:%d\r\n", ret);
- if (ret > 0) {
- for (uint32_t i = 0; i < ret; i++) {
- USB_LOG_RAW("%02x ", g_cdc_acm_buf[i]);
- }
- }
- USB_LOG_RAW("\r\n");
- }
-#endif
-
- usbh_cdc_acm_run(cdc_acm_class);
- return ret;
-}
-
-static int usbh_cdc_acm_disconnect(struct usbh_hubport *hport, uint8_t intf)
-{
- int ret = 0;
-
- struct usbh_cdc_acm *cdc_acm_class = (struct usbh_cdc_acm *)hport->config.intf[intf].priv;
-
- if (cdc_acm_class) {
- if (cdc_acm_class->bulkin) {
- usbh_kill_urb(&cdc_acm_class->bulkin_urb);
- }
-
- if (cdc_acm_class->bulkout) {
- usbh_kill_urb(&cdc_acm_class->bulkout_urb);
- }
-
-#ifdef CONFIG_USBHOST_CDC_ACM_NOTIFY
- if (cdc_acm_class->intin) {
- usbh_kill_urb(&cdc_acm_class->intin_urb);
- }
-#endif
-
- if (hport->config.intf[intf].devname[0] != '\0') {
- usb_osal_thread_schedule_other();
- USB_LOG_INFO("Unregister CDC ACM Class:%s\r\n", hport->config.intf[intf].devname);
- usbh_cdc_acm_stop(cdc_acm_class);
- }
-
- usbh_cdc_acm_class_free(cdc_acm_class);
- }
-
- return ret;
-}
-
-int usbh_cdc_acm_bulk_in_transfer(struct usbh_cdc_acm *cdc_acm_class, uint8_t *buffer, uint32_t buflen, uint32_t timeout)
-{
- int ret;
- struct usbh_urb *urb = &cdc_acm_class->bulkin_urb;
-
- usbh_bulk_urb_fill(urb, cdc_acm_class->hport, cdc_acm_class->bulkin, buffer, buflen, timeout, NULL, NULL);
- ret = usbh_submit_urb(urb);
- if (ret == 0) {
- ret = urb->actual_length;
- }
- return ret;
-}
-
-int usbh_cdc_acm_bulk_out_transfer(struct usbh_cdc_acm *cdc_acm_class, uint8_t *buffer, uint32_t buflen, uint32_t timeout)
-{
- int ret;
- struct usbh_urb *urb = &cdc_acm_class->bulkout_urb;
-
- usbh_bulk_urb_fill(urb, cdc_acm_class->hport, cdc_acm_class->bulkout, buffer, buflen, timeout, NULL, NULL);
- ret = usbh_submit_urb(urb);
- if (ret == 0) {
- ret = urb->actual_length;
- }
- return ret;
-}
-
-static int usbh_cdc_data_connect(struct usbh_hubport *hport, uint8_t intf)
-{
- (void)hport;
- (void)intf;
- return 0;
-}
-
-static int usbh_cdc_data_disconnect(struct usbh_hubport *hport, uint8_t intf)
-{
- (void)hport;
- (void)intf;
- return 0;
-}
-
-__WEAK void usbh_cdc_acm_run(struct usbh_cdc_acm *cdc_acm_class)
-{
- (void)cdc_acm_class;
-}
-
-__WEAK void usbh_cdc_acm_stop(struct usbh_cdc_acm *cdc_acm_class)
-{
- (void)cdc_acm_class;
-}
-
-const struct usbh_class_driver cdc_acm_class_driver = {
- .driver_name = "cdc_acm",
- .connect = usbh_cdc_acm_connect,
- .disconnect = usbh_cdc_acm_disconnect
-};
-
-const struct usbh_class_driver cdc_data_class_driver = {
- .driver_name = "cdc_data",
- .connect = usbh_cdc_data_connect,
- .disconnect = usbh_cdc_data_disconnect
-};
-
-CLASS_INFO_DEFINE const struct usbh_class_info cdc_acm_none_class_info = {
- .match_flags = USB_CLASS_MATCH_INTF_CLASS | USB_CLASS_MATCH_INTF_SUBCLASS | USB_CLASS_MATCH_INTF_PROTOCOL,
- .bInterfaceClass = USB_DEVICE_CLASS_CDC,
- .bInterfaceSubClass = CDC_ABSTRACT_CONTROL_MODEL,
- .bInterfaceProtocol = CDC_COMMON_PROTOCOL_NONE,
- .id_table = NULL,
- .class_driver = &cdc_acm_class_driver
-};
-
-CLASS_INFO_DEFINE const struct usbh_class_info cdc_acm_at_class_info = {
- .match_flags = USB_CLASS_MATCH_INTF_CLASS | USB_CLASS_MATCH_INTF_SUBCLASS | USB_CLASS_MATCH_INTF_PROTOCOL,
- .bInterfaceClass = USB_DEVICE_CLASS_CDC,
- .bInterfaceSubClass = CDC_ABSTRACT_CONTROL_MODEL,
- .bInterfaceProtocol = CDC_COMMON_PROTOCOL_AT_COMMANDS,
- .id_table = NULL,
- .class_driver = &cdc_acm_class_driver
-};
-
-CLASS_INFO_DEFINE const struct usbh_class_info cdc_data_class_info = {
- .match_flags = USB_CLASS_MATCH_INTF_CLASS,
- .bInterfaceClass = USB_DEVICE_CLASS_CDC_DATA,
- .bInterfaceSubClass = 0x00,
- .bInterfaceProtocol = 0x00,
- .id_table = NULL,
- .class_driver = &cdc_data_class_driver
-};
diff --git a/class/cdc/usbh_cdc_acm.h b/class/cdc/usbh_cdc_acm.h
deleted file mode 100644
index 80800bf9..00000000
--- a/class/cdc/usbh_cdc_acm.h
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright (c) 2022, sakumisu
- *
- * SPDX-License-Identifier: Apache-2.0
- */
-#ifndef USBH_CDC_ACM_H
-#define USBH_CDC_ACM_H
-
-#include "usb_cdc.h"
-
-struct usbh_cdc_acm {
- struct usbh_hubport *hport;
- struct usb_endpoint_descriptor *bulkin; /* Bulk IN endpoint */
- struct usb_endpoint_descriptor *bulkout; /* Bulk OUT endpoint */
-#ifdef CONFIG_USBHOST_CDC_ACM_NOTIFY
- struct usb_endpoint_descriptor *intin; /* INTR IN endpoint (optional) */
-#endif
- struct usbh_urb bulkout_urb;
- struct usbh_urb bulkin_urb;
-#ifdef CONFIG_USBHOST_CDC_ACM_NOTIFY
- struct usbh_urb intin_urb;
-#endif
-
- struct cdc_line_coding linecoding;
-
- uint8_t intf;
- uint8_t minor;
-
- void *user_data;
-};
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-int usbh_cdc_acm_set_line_coding(struct usbh_cdc_acm *cdc_acm_class, struct cdc_line_coding *line_coding);
-int usbh_cdc_acm_get_line_coding(struct usbh_cdc_acm *cdc_acm_class, struct cdc_line_coding *line_coding);
-int usbh_cdc_acm_set_line_state(struct usbh_cdc_acm *cdc_acm_class, bool dtr, bool rts);
-
-int usbh_cdc_acm_bulk_in_transfer(struct usbh_cdc_acm *cdc_acm_class, uint8_t *buffer, uint32_t buflen, uint32_t timeout);
-int usbh_cdc_acm_bulk_out_transfer(struct usbh_cdc_acm *cdc_acm_class, uint8_t *buffer, uint32_t buflen, uint32_t timeout);
-
-void usbh_cdc_acm_run(struct usbh_cdc_acm *cdc_acm_class);
-void usbh_cdc_acm_stop(struct usbh_cdc_acm *cdc_acm_class);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* USBH_CDC_ACM_H */
diff --git a/class/serial/usbh_cdc_acm.c b/class/serial/usbh_cdc_acm.c
new file mode 100644
index 00000000..121649b1
--- /dev/null
+++ b/class/serial/usbh_cdc_acm.c
@@ -0,0 +1,266 @@
+/*
+ * Copyright (c) 2022 ~ 2025, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#include "usbh_core.h"
+#include "usbh_serial.h"
+#include "usbh_cdc_acm.h"
+
+#undef USB_DBG_TAG
+#define USB_DBG_TAG "usbh_cdc_acm"
+#include "usb_log.h"
+
+struct usbh_cdc_acm {
+ struct usb_endpoint_descriptor *intin;
+ struct usbh_urb intin_urb;
+ struct usb_osal_timer *modem_timer;
+ uint16_t modem_status;
+};
+
+static int usbh_cdc_acm_attach(struct usbh_serial *serial)
+{
+ struct usb_endpoint_descriptor *ep_desc;
+ int ret;
+
+ struct usbh_cdc_acm *cdc_acm_class = usb_osal_malloc(sizeof(struct usbh_cdc_acm));
+ if (!cdc_acm_class) {
+ USB_LOG_ERR("No memory for cdc_acm_class\r\n");
+ return -USB_ERR_NOMEM;
+ }
+ memset(cdc_acm_class, 0, sizeof(struct usbh_cdc_acm));
+ serial->priv = cdc_acm_class;
+
+ for (uint8_t i = 0; i < serial->hport->config.intf[serial->intf].altsetting[0].intf_desc.bNumEndpoints; i++) {
+ ep_desc = &serial->hport->config.intf[serial->intf].altsetting[0].ep[i].ep_desc;
+
+ if (USB_GET_ENDPOINT_TYPE(ep_desc->bmAttributes) == USB_ENDPOINT_TYPE_INTERRUPT) {
+ if (ep_desc->bEndpointAddress & 0x80) {
+ USBH_EP_INIT(cdc_acm_class->intin, ep_desc);
+ break;
+ } else {
+ }
+ }
+ }
+
+ if (!cdc_acm_class->intin) {
+ USB_LOG_ERR("Failed to find interrupt endpoint\r\n");
+ ret = -USB_ERR_NODEV;
+ goto errout;
+ }
+ return 0;
+errout:
+ serial->priv = NULL;
+ usb_osal_free(cdc_acm_class);
+ return ret;
+}
+
+static void usbh_cdc_acm_detach(struct usbh_serial *serial)
+{
+ struct usbh_cdc_acm *cdc_acm_class;
+
+ if (!serial || !serial->priv) {
+ return;
+ }
+
+ cdc_acm_class = (struct usbh_cdc_acm *)serial->priv;
+ if (cdc_acm_class->intin) {
+ usbh_kill_urb(&cdc_acm_class->intin_urb);
+ }
+ serial->priv = NULL;
+ usb_osal_free(cdc_acm_class);
+}
+
+static int usbh_cdc_acm_set_line_coding(struct usbh_serial *serial, struct cdc_line_coding *line_coding)
+{
+ struct usb_setup_packet *setup;
+
+ if (!serial || !serial->hport) {
+ return -USB_ERR_INVAL;
+ }
+ setup = serial->hport->setup;
+
+ setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE;
+ setup->bRequest = CDC_REQUEST_SET_LINE_CODING;
+ setup->wValue = 0;
+ setup->wIndex = serial->intf;
+ setup->wLength = 7;
+
+ memcpy(serial->iobuffer, line_coding, sizeof(struct cdc_line_coding));
+
+ return usbh_control_transfer(serial->hport, setup, serial->iobuffer);
+}
+
+static int usbh_cdc_acm_get_line_coding(struct usbh_serial *serial, struct cdc_line_coding *line_coding)
+{
+ struct usb_setup_packet *setup;
+ int ret;
+
+ if (!serial || !serial->hport) {
+ return -USB_ERR_INVAL;
+ }
+ setup = serial->hport->setup;
+
+ setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE;
+ setup->bRequest = CDC_REQUEST_GET_LINE_CODING;
+ setup->wValue = 0;
+ setup->wIndex = serial->intf;
+ setup->wLength = 7;
+
+ ret = usbh_control_transfer(serial->hport, setup, serial->iobuffer);
+ if (ret < 0) {
+ return ret;
+ }
+ memcpy(line_coding, serial->iobuffer, sizeof(struct cdc_line_coding));
+ return ret;
+}
+
+static int usbh_cdc_acm_set_line_state(struct usbh_serial *serial, bool dtr, bool rts)
+{
+ struct usb_setup_packet *setup;
+
+ if (!serial || !serial->hport) {
+ return -USB_ERR_INVAL;
+ }
+ setup = serial->hport->setup;
+
+ setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE;
+ setup->bRequest = CDC_REQUEST_SET_CONTROL_LINE_STATE;
+ setup->wValue = (dtr << 0) | (rts << 1);
+ setup->wIndex = serial->intf;
+ setup->wLength = 0;
+
+ return usbh_control_transfer(serial->hport, setup, NULL);
+}
+
+static int usbh_cdc_acm_get_modem_status(struct usbh_serial *serial)
+{
+ struct usbh_cdc_acm *cdc_acm_class;
+ uintptr_t flags;
+ uint16_t status;
+
+ if (!serial || !serial->hport || !serial->priv) {
+ return -USB_ERR_INVAL;
+ }
+
+ flags = usb_osal_enter_critical_section();
+
+ cdc_acm_class = (struct usbh_cdc_acm *)serial->priv;
+
+ status = (cdc_acm_class->modem_status & CDC_SERIAL_STATE_TX_CARRIER ? USBH_SERIAL_TIOCM_DSR : 0) |
+ (cdc_acm_class->modem_status & CDC_SERIAL_STATE_RING ? USBH_SERIAL_TIOCM_RI : 0) |
+ (cdc_acm_class->modem_status & CDC_SERIAL_STATE_RX_CARRIER ? USBH_SERIAL_TIOCM_CD : 0) |
+ (serial->line_state & USBH_SERIAL_TIOCM_DTR ? USBH_SERIAL_TIOCM_DTR : 0) |
+ (serial->line_state & USBH_SERIAL_TIOCM_RTS ? USBH_SERIAL_TIOCM_RTS : 0);
+
+ usb_osal_leave_critical_section(flags);
+
+ return status;
+}
+
+#ifdef CONFIG_USBH_SERIAL_GET_MODEM_STATUS
+static int __usbh_cdc_acm_get_modem_status(struct usbh_serial *serial)
+{
+ struct usbh_cdc_acm *cdc_acm_class;
+ struct cdc_acm_notification *notification;
+ uint16_t difference;
+ uintptr_t flags;
+ int ret;
+
+ if (!serial || !serial->hport || !serial->priv) {
+ return -USB_ERR_INVAL;
+ }
+ cdc_acm_class = (struct usbh_cdc_acm *)serial->priv;
+
+ usbh_int_urb_fill(&cdc_acm_class->intin_urb, serial->hport, cdc_acm_class->intin, &serial->iobuffer[USBH_SERIAL_INT_NOCACHE_OFFSET], cdc_acm_class->intin->wMaxPacketSize, 0xffffffff, NULL, NULL);
+ ret = usbh_submit_urb(&cdc_acm_class->intin_urb);
+ if (ret < 0) {
+ return ret;
+ }
+
+ if (cdc_acm_class->intin_urb.actual_length < sizeof(struct cdc_acm_notification)) {
+ return -USB_ERR_INVAL;
+ }
+
+ notification = (struct cdc_acm_notification *)&serial->iobuffer[USBH_SERIAL_INT_NOCACHE_OFFSET];
+ if (notification->bNotificationType != CDC_NOTIFICATION_SERIAL_STATE) {
+ return -USB_ERR_INVAL;
+ }
+
+ flags = usb_osal_enter_critical_section();
+
+ difference = cdc_acm_class->modem_status ^ notification->data;
+ cdc_acm_class->modem_status = notification->data;
+
+ if (difference & CDC_SERIAL_STATE_TX_CARRIER)
+ serial->iocount.dsr++;
+ if (difference & CDC_SERIAL_STATE_RX_CARRIER)
+ serial->iocount.dsr++;
+ if (notification->data & CDC_SERIAL_STATE_BREAK)
+ serial->iocount.brk++;
+ if (notification->data & CDC_SERIAL_STATE_FRAMING)
+ serial->iocount.frame++;
+ if (notification->data & CDC_SERIAL_STATE_PARITY)
+ serial->iocount.parity++;
+ if (notification->data & CDC_SERIAL_STATE_OVERRUN)
+ serial->iocount.overrun++;
+
+ usb_osal_leave_critical_section(flags);
+
+ return ret;
+}
+#endif
+
+static const struct usbh_serial_driver cdc_acm_driver = {
+ .driver_name = "cdc_acm",
+
+ .ignore_rx_header = 0,
+ .ignore_tx_header = 0,
+
+ .attach = usbh_cdc_acm_attach,
+ .detach = usbh_cdc_acm_detach,
+ .set_flow_control = NULL,
+ .set_line_coding = usbh_cdc_acm_set_line_coding,
+ .get_line_coding = usbh_cdc_acm_get_line_coding,
+ .set_line_state = usbh_cdc_acm_set_line_state,
+ .get_modem_status = usbh_cdc_acm_get_modem_status,
+};
+
+static int usbh_cdc_acm_connect(struct usbh_hubport *hport, uint8_t intf)
+{
+ return usbh_serial_probe(hport, intf, &cdc_acm_driver) ? 0 : -USB_ERR_NOMEM;
+}
+
+static int usbh_cdc_acm_disconnect(struct usbh_hubport *hport, uint8_t intf)
+{
+ struct usbh_serial *serial = (struct usbh_serial *)hport->config.intf[intf].priv;
+
+ if (serial) {
+ usbh_serial_remove(serial);
+ }
+ return 0;
+}
+
+const struct usbh_class_driver cdc_acm_class_driver = {
+ .driver_name = "cdc_acm",
+ .connect = usbh_cdc_acm_connect,
+ .disconnect = usbh_cdc_acm_disconnect
+};
+
+CLASS_INFO_DEFINE const struct usbh_class_info cdc_acm_none_class_info = {
+ .match_flags = USB_CLASS_MATCH_INTF_CLASS | USB_CLASS_MATCH_INTF_SUBCLASS | USB_CLASS_MATCH_INTF_PROTOCOL,
+ .bInterfaceClass = USB_DEVICE_CLASS_CDC,
+ .bInterfaceSubClass = CDC_ABSTRACT_CONTROL_MODEL,
+ .bInterfaceProtocol = CDC_COMMON_PROTOCOL_NONE,
+ .id_table = NULL,
+ .class_driver = &cdc_acm_class_driver
+};
+
+CLASS_INFO_DEFINE const struct usbh_class_info cdc_acm_at_class_info = {
+ .match_flags = USB_CLASS_MATCH_INTF_CLASS | USB_CLASS_MATCH_INTF_SUBCLASS | USB_CLASS_MATCH_INTF_PROTOCOL,
+ .bInterfaceClass = USB_DEVICE_CLASS_CDC,
+ .bInterfaceSubClass = CDC_ABSTRACT_CONTROL_MODEL,
+ .bInterfaceProtocol = CDC_COMMON_PROTOCOL_AT_COMMANDS,
+ .id_table = NULL,
+ .class_driver = &cdc_acm_class_driver
+};
diff --git a/class/serial/usbh_cdc_acm.h b/class/serial/usbh_cdc_acm.h
new file mode 100644
index 00000000..2940cd9c
--- /dev/null
+++ b/class/serial/usbh_cdc_acm.h
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2022 ~ 2025, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#ifndef USBH_CDC_ACM_H
+#define USBH_CDC_ACM_H
+
+#include "usb_cdc.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* USBH_CDC_ACM_H */
diff --git a/class/serial/usbh_ch34x.c b/class/serial/usbh_ch34x.c
new file mode 100644
index 00000000..2bc3df10
--- /dev/null
+++ b/class/serial/usbh_ch34x.c
@@ -0,0 +1,410 @@
+/*
+ * Copyright (c) 2024 ~ 2025, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#include "usbh_core.h"
+#include "usbh_serial.h"
+#include "usbh_ch34x.h"
+
+#undef USB_DBG_TAG
+#define USB_DBG_TAG "usbh_ch43x"
+#include "usb_log.h"
+
+struct usbh_ch34x {
+ struct usb_endpoint_descriptor *intin;
+ struct usbh_urb intin_urb;
+ struct usb_osal_timer *modem_timer;
+ uint16_t modem_status;
+};
+
+/* refer to https://github.com/WCHSoftGroup/ch341ser_linux/blob/main/driver/ch341.c */
+
+static int usbh_ch34x_get_baudrate_div(uint32_t baudrate, uint8_t *factor, uint8_t *divisor)
+{
+ uint8_t a;
+ uint8_t b;
+ uint32_t c;
+
+ switch (baudrate) {
+ case 921600:
+ a = 0xf3;
+ b = 7;
+ break;
+
+ case 307200:
+ a = 0xd9;
+ b = 7;
+ break;
+
+ default:
+ if (baudrate > 6000000 / 255) {
+ b = 3;
+ c = 6000000;
+ } else if (baudrate > 750000 / 255) {
+ b = 2;
+ c = 750000;
+ } else if (baudrate > 93750 / 255) {
+ b = 1;
+ c = 93750;
+ } else {
+ b = 0;
+ c = 11719;
+ }
+ a = (uint8_t)(c / baudrate);
+ if (a == 0 || a == 0xFF) {
+ return -USB_ERR_INVAL;
+ }
+ if ((c / a - baudrate) > (baudrate - c / (a + 1))) {
+ a++;
+ }
+ a = (uint8_t)(256 - a);
+ break;
+ }
+
+ *factor = a;
+ *divisor = b;
+
+ return 0;
+}
+
+static int usbh_ch34x_control_out(struct usbh_serial *serial, uint8_t bRequest, uint16_t wValue, uint16_t wIndex)
+{
+ struct usb_setup_packet *setup;
+
+ if (!serial || !serial->hport) {
+ return -USB_ERR_INVAL;
+ }
+ setup = serial->hport->setup;
+
+ setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE;
+ setup->bRequest = bRequest;
+ setup->wValue = wValue;
+ setup->wIndex = wIndex;
+ setup->wLength = 0;
+
+ return usbh_control_transfer(serial->hport, setup, NULL);
+}
+
+static int usbh_ch34x_control_in(struct usbh_serial *serial, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, uint8_t *data, uint16_t size)
+{
+ struct usb_setup_packet *setup;
+ int ret;
+
+ if (!serial || !serial->hport) {
+ return -USB_ERR_INVAL;
+ }
+ setup = serial->hport->setup;
+
+ setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE;
+ setup->bRequest = bRequest;
+ setup->wValue = wValue;
+ setup->wIndex = wIndex;
+ setup->wLength = size;
+
+ ret = usbh_control_transfer(serial->hport, setup, serial->iobuffer);
+ if (ret < 0) {
+ return ret;
+ }
+ memcpy(data, serial->iobuffer, size);
+
+ return ret;
+}
+
+static int usbh_ch34x_get_version(struct usbh_serial *serial)
+{
+ int ret;
+ uint8_t buf[2];
+
+ ret = usbh_ch34x_control_in(serial, CH34X_READ_VERSION, 0, 0, buf, 2);
+ if (ret < 0) {
+ return ret;
+ }
+
+ USB_LOG_INFO("chip version: 0x%02x\r\n", buf[0]);
+ return ret;
+}
+
+static int usbh_ch34x_attach(struct usbh_serial *serial)
+{
+ struct usb_endpoint_descriptor *ep_desc;
+ int ret;
+
+ struct usbh_ch34x *ch34x_class = usb_osal_malloc(sizeof(struct usbh_ch34x));
+ if (!ch34x_class) {
+ USB_LOG_ERR("No memory for ch34x_class\r\n");
+ return -USB_ERR_NOMEM;
+ }
+ memset(ch34x_class, 0, sizeof(struct usbh_ch34x));
+ serial->priv = ch34x_class;
+
+ for (uint8_t i = 0; i < serial->hport->config.intf[serial->intf].altsetting[0].intf_desc.bNumEndpoints; i++) {
+ ep_desc = &serial->hport->config.intf[serial->intf].altsetting[0].ep[i].ep_desc;
+
+ if (USB_GET_ENDPOINT_TYPE(ep_desc->bmAttributes) == USB_ENDPOINT_TYPE_INTERRUPT) {
+ if (ep_desc->bEndpointAddress & 0x80) {
+ USBH_EP_INIT(ch34x_class->intin, ep_desc);
+ break;
+ } else {
+ }
+ }
+ }
+
+ if (!ch34x_class->intin) {
+ USB_LOG_ERR("Failed to find interrupt endpoint\r\n");
+ ret = -USB_ERR_NODEV;
+ goto errout;
+ }
+
+ ret = usbh_ch34x_get_version(serial);
+ ret |= usbh_ch34x_control_out(serial, CH34X_SERIAL_INIT, 0, 0);
+ ret |= usbh_ch34x_control_out(serial, CH34X_WRITE_REG, 0x1312, 0xd982);
+ ret |= usbh_ch34x_control_out(serial, CH34X_WRITE_REG, 0x0f2c, 0x0007);
+ if (ret < 0) {
+ goto errout;
+ }
+
+ return 0;
+errout:
+ serial->priv = NULL;
+ usb_osal_free(ch34x_class);
+ return ret;
+}
+
+static void usbh_ch34x_detach(struct usbh_serial *serial)
+{
+ struct usbh_ch34x *ch34x_class;
+
+ if (!serial || !serial->priv) {
+ return;
+ }
+
+ ch34x_class = (struct usbh_ch34x *)serial->priv;
+ if (ch34x_class->intin) {
+ usbh_kill_urb(&ch34x_class->intin_urb);
+ }
+ serial->priv = NULL;
+ usb_osal_free(ch34x_class);
+}
+
+static int usbh_ch34x_set_flow_ctrl(struct usbh_serial *serial, bool hardctrl)
+{
+ return usbh_ch34x_control_out(serial, CH34X_WRITE_REG, 0x2727, hardctrl ? 0x0101 : 0x0000);
+}
+
+static int usbh_ch34x_set_line_coding(struct usbh_serial *serial, struct cdc_line_coding *line_coding)
+{
+ uint16_t reg_value = 0;
+ uint16_t value = 0;
+ uint16_t index = 0;
+ uint8_t factor = 0;
+ uint8_t divisor = 0;
+
+ switch (line_coding->bParityType) {
+ case 0:
+ break;
+ case 1:
+ reg_value |= CH341_L_PO;
+ break;
+ case 2:
+ reg_value |= CH341_L_PE;
+ break;
+ case 3:
+ reg_value |= CH341_L_PM;
+ break;
+ case 4:
+ reg_value |= CH341_L_PS;
+ break;
+ default:
+ return -USB_ERR_INVAL;
+ }
+
+ switch (line_coding->bDataBits) {
+ case 5:
+ reg_value |= CH341_L_D5;
+ break;
+ case 6:
+ reg_value |= CH341_L_D6;
+ break;
+ case 7:
+ reg_value |= CH341_L_D7;
+ break;
+ case 8:
+ reg_value |= CH341_L_D8;
+ break;
+ default:
+ return -USB_ERR_INVAL;
+ }
+
+ if (line_coding->bCharFormat == 2) {
+ reg_value |= CH341_L_SB;
+ }
+
+ reg_value |= 0xC0;
+
+ value |= 0x9c;
+ value |= reg_value << 8;
+ index |= 0x80 | divisor;
+ index |= (uint16_t)factor << 8;
+
+ usbh_ch34x_get_baudrate_div(line_coding->dwDTERate, &factor, &divisor);
+
+ return usbh_ch34x_control_out(serial, CH34X_SERIAL_INIT, value, index);
+}
+
+static int usbh_ch34x_set_line_state(struct usbh_serial *serial, bool dtr, bool rts)
+{
+ uint16_t value = 0;
+ uint8_t control = 0;
+
+ control = (dtr << 5) | (rts << 6);
+ value = (uint8_t)~control;
+
+ return usbh_ch34x_control_out(serial, CH34X_MODEM_CTRL, value, 0x0000);
+}
+
+static int usbh_ch34x_get_modem_status(struct usbh_serial *serial)
+{
+ struct usbh_ch34x *ch34x_class;
+ uintptr_t flags;
+ uint16_t status;
+
+ if (!serial || !serial->hport || !serial->priv) {
+ return -USB_ERR_INVAL;
+ }
+
+ flags = usb_osal_enter_critical_section();
+
+ ch34x_class = (struct usbh_ch34x *)serial->priv;
+
+ status = (ch34x_class->modem_status & CH341_CTI_DS ? USBH_SERIAL_TIOCM_DSR : 0) |
+ (ch34x_class->modem_status & CH341_CTI_C ? USBH_SERIAL_TIOCM_CTS : 0) |
+ (ch34x_class->modem_status & CH341_CTRL_RI ? USBH_SERIAL_TIOCM_RI : 0) |
+ (ch34x_class->modem_status & CH341_CTI_DC ? USBH_SERIAL_TIOCM_CD : 0) |
+ (serial->line_state & USBH_SERIAL_TIOCM_DTR ? USBH_SERIAL_TIOCM_DTR : 0) |
+ (serial->line_state & USBH_SERIAL_TIOCM_RTS ? USBH_SERIAL_TIOCM_RTS : 0);
+
+ usb_osal_leave_critical_section(flags);
+
+ return status;
+}
+
+#ifdef CONFIG_USBH_SERIAL_GET_MODEM_STATUS
+static int __usbh_ch34x_get_modem_status(struct usbh_serial *serial, uint16_t *status)
+{
+ struct usbh_ch34x *ch34x_class;
+ uint8_t type = 0;
+ uint8_t data = 0;
+ uint16_t difference;
+ uintptr_t flags;
+ int ret;
+
+ if (!serial || !serial->hport || !serial->priv) {
+ return -USB_ERR_INVAL;
+ }
+ ch34x_class = (struct usbh_ch34x *)serial->priv;
+
+ usbh_int_urb_fill(&ch34x_class->intin_urb, serial->hport, ch34x_class->intin, &serial->iobuffer[USBH_SERIAL_INT_NOCACHE_OFFSET], ch34x_class->intin->wMaxPacketSize, 0xffffffff, NULL, NULL);
+ ret = usbh_submit_urb(&ch34x_class->intin_urb);
+ if (ret < 0) {
+ return ret;
+ }
+
+ if (ret < 4) {
+ return -USB_ERR_INVAL;
+ }
+
+ flags = usb_osal_enter_critical_section();
+
+ type = serial->iobuffer[USBH_SERIAL_INT_NOCACHE_OFFSET];
+ if (type & CH341_CTT_M) {
+ data = ~serial->iobuffer[USBH_SERIAL_INT_NOCACHE_OFFSET + 2] & CH341_CTI_ST;
+ difference = data ^ (ch34x_class->modem_status & CH341_CTI_ST);
+ ch34x_class->modem_status = data;
+
+ if (difference) {
+ if (difference & CH341_CTI_C) {
+ serial->iocount.cts++;
+ }
+ if (difference & CH341_CTI_DS) {
+ serial->iocount.dsr++;
+ }
+ if (difference & CH341_CTRL_RI) {
+ serial->iocount.rng++;
+ }
+ if (difference & CH341_CTI_DC) {
+ serial->iocount.dcd++;
+ }
+ }
+ }
+
+ if (type & CH341_CTT_O) {
+ serial->iocount.overrun++;
+ }
+ if ((type & CH341_CTT_F) == CH341_CTT_F) {
+ serial->iocount.frame++;
+ }
+ if (type & CH341_CTT_P) {
+ serial->iocount.parity++;
+ }
+
+ usb_osal_leave_critical_section(flags);
+
+ return ret;
+}
+#endif
+
+static const struct usbh_serial_driver ch34x_driver = {
+ .driver_name = "ch34x",
+
+ .ignore_rx_header = 0,
+ .ignore_tx_header = 0,
+
+ .attach = usbh_ch34x_attach,
+ .detach = usbh_ch34x_detach,
+ .set_flow_control = usbh_ch34x_set_flow_ctrl,
+ .set_line_coding = usbh_ch34x_set_line_coding,
+ .get_line_coding = NULL,
+ .set_line_state = usbh_ch34x_set_line_state,
+ .get_modem_status = usbh_ch34x_get_modem_status,
+};
+
+static int usbh_ch34x_connect(struct usbh_hubport *hport, uint8_t intf)
+{
+ return usbh_serial_probe(hport, intf, &ch34x_driver) ? 0 : -USB_ERR_NOMEM;
+}
+
+static int usbh_ch34x_disconnect(struct usbh_hubport *hport, uint8_t intf)
+{
+ struct usbh_serial *serial = (struct usbh_serial *)hport->config.intf[intf].priv;
+
+ if (serial) {
+ usbh_serial_remove(serial);
+ }
+
+ return 0;
+}
+
+static const uint16_t ch34x_id_table[][2] = {
+ { 0x1A86, 0x7523 }, /* ch340 chip */
+ { 0x1A86, 0x7522 }, /* ch340k chip */
+ { 0x1A86, 0x5523 }, /* ch341 chip */
+ { 0x1A86, 0xe523 }, /* ch330 chip */
+ { 0x4348, 0x5523 }, /* ch340 custom chip */
+ { 0, 0 },
+};
+
+const struct usbh_class_driver ch34x_class_driver = {
+ .driver_name = "ch34x",
+ .connect = usbh_ch34x_connect,
+ .disconnect = usbh_ch34x_disconnect
+};
+
+CLASS_INFO_DEFINE const struct usbh_class_info ch34x_class_info = {
+ .match_flags = USB_CLASS_MATCH_VID_PID | USB_CLASS_MATCH_INTF_CLASS,
+ .bInterfaceClass = 0xff,
+ .bInterfaceSubClass = 0x00,
+ .bInterfaceProtocol = 0x00,
+ .id_table = ch34x_id_table,
+ .class_driver = &ch34x_class_driver
+}; \ No newline at end of file
diff --git a/class/serial/usbh_ch34x.h b/class/serial/usbh_ch34x.h
new file mode 100644
index 00000000..21e48101
--- /dev/null
+++ b/class/serial/usbh_ch34x.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2024 ~ 2025, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#ifndef USBH_CH34X_H
+#define USBH_CH34X_H
+
+#include "usb_cdc.h"
+
+/* Requests */
+#define CH34X_READ_VERSION 0x5F
+#define CH34X_WRITE_REG 0x9A
+#define CH34X_READ_REG 0x95
+#define CH34X_SERIAL_INIT 0xA1
+#define CH34X_MODEM_CTRL 0xA4
+
+// modem control bits
+#define CH34X_BIT_RTS (1 << 6)
+#define CH34X_BIT_DTR (1 << 5)
+
+#define CH341_CTO_O 0x10
+#define CH341_CTO_D 0x20
+#define CH341_CTO_R 0x40
+#define CH341_CTI_C 0x01
+#define CH341_CTI_DS 0x02
+#define CH341_CTRL_RI 0x04
+#define CH341_CTI_DC 0x08
+#define CH341_CTI_ST 0x0f
+
+#define CH341_CTT_M BIT(3)
+#define CH341_CTT_F (BIT(2) | BIT(6))
+#define CH341_CTT_P BIT(2)
+#define CH341_CTT_O BIT(1)
+
+#define CH341_L_ER 0x80
+#define CH341_L_ET 0x40
+#define CH341_L_PS 0x38
+#define CH341_L_PM 0x28
+#define CH341_L_PE 0x18
+#define CH341_L_PO 0x08
+#define CH341_L_SB 0x04
+#define CH341_L_D8 0x03
+#define CH341_L_D7 0x02
+#define CH341_L_D6 0x01
+#define CH341_L_D5 0x00
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* USBH_CH34X_H */
diff --git a/class/serial/usbh_cp210x.c b/class/serial/usbh_cp210x.c
new file mode 100644
index 00000000..1aa52ff5
--- /dev/null
+++ b/class/serial/usbh_cp210x.c
@@ -0,0 +1,510 @@
+/*
+ * Copyright (c) 2024 ~ 2025, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#include "usbh_core.h"
+#include "usbh_serial.h"
+#include "usbh_cp210x.h"
+
+#undef USB_DBG_TAG
+#define USB_DBG_TAG "usbh_cp210x"
+#include "usb_log.h"
+
+struct usbh_cp210x {
+ uint8_t partnum;
+ uint32_t fw_version;
+ uint32_t min_speed;
+ uint32_t max_speed;
+ bool use_actual_rate;
+ bool no_flow_control;
+ bool no_event_mode;
+};
+
+struct cp210x_rate {
+ uint32_t rate;
+ uint32_t high;
+};
+
+static const struct cp210x_rate cp210x_an205_table1[] = {
+ { 300, 300 },
+ { 600, 600 },
+ { 1200, 1200 },
+ { 1800, 1800 },
+ { 2400, 2400 },
+ { 4000, 4000 },
+ { 4800, 4803 },
+ { 7200, 7207 },
+ { 9600, 9612 },
+ { 14400, 14428 },
+ { 16000, 16062 },
+ { 19200, 19250 },
+ { 28800, 28912 },
+ { 38400, 38601 },
+ { 51200, 51558 },
+ { 56000, 56280 },
+ { 57600, 58053 },
+ { 64000, 64111 },
+ { 76800, 77608 },
+ { 115200, 117028 },
+ { 128000, 129347 },
+ { 153600, 156868 },
+ { 230400, 237832 },
+ { 250000, 254234 },
+ { 256000, 273066 },
+ { 460800, 491520 },
+ { 500000, 567138 },
+ { 576000, 670254 },
+ { 921600, 0xffffffff }
+};
+
+/*
+ * Quantises the baud rate as per AN205 Table 1
+ */
+static uint32_t cp210x_get_an205_rate(uint32_t baud)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cp210x_an205_table1); ++i) {
+ if (baud <= cp210x_an205_table1[i].high)
+ break;
+ }
+
+ return cp210x_an205_table1[i].rate;
+}
+
+static uint32_t cp210x_get_actual_rate(uint32_t baud)
+{
+ unsigned int prescale = 1;
+ unsigned int div;
+
+ if (baud <= 365)
+ prescale = 4;
+
+ div = DIV_ROUND_CLOSEST(48000000, 2 * prescale * baud);
+ baud = 48000000 / (2 * prescale * div);
+
+ return baud;
+}
+
+static void usbh_cp210x_init_max_speed(struct usbh_serial *serial)
+{
+ struct usbh_cp210x *cp210x_class;
+
+ if (!serial || !serial->hport || !serial->priv) {
+ return;
+ }
+
+ cp210x_class = (struct usbh_cp210x *)serial->priv;
+
+ bool use_actual_rate = false;
+ uint32_t min = 300;
+ uint32_t max;
+
+ switch (cp210x_class->partnum) {
+ case CP210X_PARTNUM_CP2101:
+ max = 921600;
+ break;
+ case CP210X_PARTNUM_CP2102:
+ case CP210X_PARTNUM_CP2103:
+ max = 1000000;
+ break;
+ case CP210X_PARTNUM_CP2104:
+ use_actual_rate = true;
+ max = 2000000;
+ break;
+ case CP210X_PARTNUM_CP2108:
+ max = 2000000;
+ break;
+ case CP210X_PARTNUM_CP2105:
+ if (serial->intf == 0) {
+ use_actual_rate = true;
+ max = 2000000; /* ECI */
+ } else {
+ min = 2400;
+ max = 921600; /* SCI */
+ }
+ break;
+ case CP210X_PARTNUM_CP2102N_QFN28:
+ case CP210X_PARTNUM_CP2102N_QFN24:
+ case CP210X_PARTNUM_CP2102N_QFN20:
+ use_actual_rate = true;
+ max = 3000000;
+ break;
+ default:
+ max = 2000000;
+ break;
+ }
+
+ cp210x_class->min_speed = min;
+ cp210x_class->max_speed = max;
+ cp210x_class->use_actual_rate = use_actual_rate;
+}
+
+static int usbh_cp210x_control_out(struct usbh_serial *serial, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, uint8_t *data, uint16_t size)
+{
+ struct usb_setup_packet *setup;
+
+ if (!serial || !serial->hport) {
+ return -USB_ERR_INVAL;
+ }
+ setup = serial->hport->setup;
+
+ setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE;
+ setup->bRequest = bRequest;
+ setup->wValue = wValue;
+ setup->wIndex = wIndex;
+ setup->wLength = size;
+
+ if (data && size) {
+ memcpy(serial->iobuffer, data, size);
+ return usbh_control_transfer(serial->hport, setup, serial->iobuffer);
+ } else {
+ return usbh_control_transfer(serial->hport, setup, NULL);
+ }
+}
+
+static int usbh_cp210x_control_in(struct usbh_serial *serial, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, uint8_t *data, uint16_t size)
+{
+ struct usb_setup_packet *setup;
+ int ret;
+
+ if (!serial || !serial->hport) {
+ return -USB_ERR_INVAL;
+ }
+ setup = serial->hport->setup;
+
+ setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE;
+ setup->bRequest = bRequest;
+ setup->wValue = wValue;
+ setup->wIndex = wIndex;
+ setup->wLength = size;
+
+ ret = usbh_control_transfer(serial->hport, setup, serial->iobuffer);
+ if (ret < 0) {
+ return ret;
+ }
+ memcpy(data, serial->iobuffer, size);
+
+ return ret;
+}
+
+static int usbh_cp210x_get_partnum(struct usbh_serial *serial)
+{
+ uint8_t version[3];
+ struct usbh_cp210x *cp210x_class;
+ int ret;
+
+ if (!serial || !serial->hport || !serial->priv) {
+ return -USB_ERR_INVAL;
+ }
+
+ cp210x_class = (struct usbh_cp210x *)serial->priv;
+
+ ret = usbh_cp210x_control_in(serial, CP210X_VENDOR_SPECIFIC, CP210X_GET_PARTNUM, serial->intf, (uint8_t *)&cp210x_class->partnum, 1);
+ if (ret < 0) {
+ return ret;
+ }
+
+ USB_LOG_INFO("chip partnum: 0x%02x\r\n", cp210x_class->partnum);
+
+ switch (cp210x_class->partnum) {
+ case CP210X_PARTNUM_CP2102:
+ break;
+ case CP210X_PARTNUM_CP2105:
+ case CP210X_PARTNUM_CP2108:
+ ret = usbh_cp210x_control_in(serial, CP210X_VENDOR_SPECIFIC, CP210X_GET_FW_VER_2N, serial->intf, version, 3);
+ if (ret < 0) {
+ return ret;
+ }
+ cp210x_class->fw_version = version[0] << 16 | version[1] << 8 | version[2];
+ break;
+ case CP210X_PARTNUM_CP2102N_QFN28:
+ case CP210X_PARTNUM_CP2102N_QFN24:
+ case CP210X_PARTNUM_CP2102N_QFN20:
+ ret = usbh_cp210x_control_in(serial, CP210X_VENDOR_SPECIFIC, CP210X_GET_FW_VER_2N, serial->intf, version, 3);
+ if (ret < 0) {
+ return ret;
+ }
+ cp210x_class->fw_version = version[0] << 16 | version[1] << 8 | version[2];
+ if (cp210x_class->fw_version <= 0x10004)
+ cp210x_class->no_flow_control = true;
+ break;
+ default:
+ break;
+ }
+ return ret;
+}
+
+static int usbh_cp210x_enable(struct usbh_serial *serial)
+{
+ return usbh_cp210x_control_out(serial, CP210X_IFC_ENABLE, 1, serial->intf, NULL, 0);
+}
+
+static int usbh_cp210x_set_chars(struct usbh_serial *serial)
+{
+ struct cp210x_special_chars chars = { 0 };
+
+ return usbh_cp210x_control_out(serial, CP210X_SET_CHARS, 0, serial->intf, (uint8_t *)&chars, sizeof(struct cp210x_special_chars));
+}
+
+// static int usbh_cp210x_get_common_status(struct usbh_serial *serial, struct cp210x_comm_status *status)
+// {
+// return usbh_cp210x_control_in(serial, CP210X_GET_COMM_STATUS, 0, serial->intf, (uint8_t *)status, sizeof(struct cp210x_comm_status));
+// }
+
+static int usbh_cp210x_set_baudrate(struct usbh_serial *serial, uint32_t baudrate)
+{
+ struct usb_setup_packet *setup;
+ struct usbh_cp210x *cp210x_class;
+
+ if (!serial || !serial->hport || !serial->priv) {
+ return -USB_ERR_INVAL;
+ }
+ setup = serial->hport->setup;
+ cp210x_class = (struct usbh_cp210x *)serial->priv;
+
+ setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_INTERFACE;
+ setup->bRequest = CP210X_SET_BAUDRATE;
+ setup->wValue = 0;
+ setup->wIndex = serial->intf;
+ setup->wLength = 4;
+
+ if (cp210x_class->use_actual_rate)
+ baudrate = cp210x_get_actual_rate(baudrate);
+ else if (baudrate < 1000000)
+ baudrate = cp210x_get_an205_rate(baudrate);
+
+ memcpy(serial->iobuffer, (uint8_t *)&baudrate, 4);
+ return usbh_control_transfer(serial->hport, setup, serial->iobuffer);
+}
+
+static int usbh_cp210x_set_data_format(struct usbh_serial *serial, uint8_t databits, uint8_t parity, uint8_t stopbits)
+{
+ struct usb_setup_packet *setup;
+ uint16_t value;
+
+ if (!serial || !serial->hport) {
+ return -USB_ERR_INVAL;
+ }
+ setup = serial->hport->setup;
+
+ value = ((databits & 0x0F) << 8) | ((parity & 0x0f) << 4) | ((stopbits & 0x03) << 0);
+
+ setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_INTERFACE;
+ setup->bRequest = CP210X_SET_LINE_CTL;
+ setup->wValue = value;
+ setup->wIndex = serial->intf;
+ setup->wLength = 0;
+
+ return usbh_control_transfer(serial->hport, setup, NULL);
+}
+
+static int usbh_cp210x_attach(struct usbh_serial *serial)
+{
+ struct cp210x_comm_status status = { 0 };
+ int ret;
+
+ struct usbh_cp210x *cp210x_class = usb_osal_malloc(sizeof(struct usbh_cp210x));
+ if (!cp210x_class) {
+ return -USB_ERR_NOMEM;
+ }
+ memset(cp210x_class, 0, sizeof(struct usbh_cp210x));
+ serial->priv = cp210x_class;
+
+ ret = usbh_cp210x_get_partnum(serial);
+ usbh_cp210x_init_max_speed(serial);
+ ret |= usbh_cp210x_enable(serial);
+ ret |= usbh_cp210x_set_chars(serial);
+ if (ret < 0) {
+ goto errout;
+ }
+
+ USB_LOG_INFO("ulAmountInInQueue: %u, ulAmountInOutQueue: %u\r\n", (unsigned int)status.ulAmountInInQueue, (unsigned int)status.ulAmountInOutQueue);
+ return 0;
+errout:
+ serial->priv = NULL;
+ usb_osal_free(cp210x_class);
+ return ret;
+}
+
+static void usbh_cp210x_detach(struct usbh_serial *serial)
+{
+ if (serial && serial->priv) {
+ serial->priv = NULL;
+ usb_osal_free(serial->priv);
+ }
+}
+
+int usbh_cp210x_set_flow_ctrl(struct usbh_serial *serial, bool enable)
+{
+ struct cp210x_flow_ctl flow_ctl = { 0 };
+ uint32_t flow_repl;
+ uint32_t ctl_hs;
+ int ret;
+
+ ret = usbh_cp210x_control_in(serial, CP210X_GET_FLOW, 0, serial->intf, (uint8_t *)&flow_ctl, sizeof(struct cp210x_flow_ctl));
+ if (ret < 0) {
+ return ret;
+ }
+
+ ctl_hs = flow_ctl.lControlHandshake;
+ flow_repl = flow_ctl.lFlowReplace;
+
+ ctl_hs &= ~CP210X_SERIAL_DSR_HANDSHAKE;
+ ctl_hs &= ~CP210X_SERIAL_DCD_HANDSHAKE;
+ ctl_hs &= ~CP210X_SERIAL_DSR_SENSITIVITY;
+ ctl_hs &= ~CP210X_SERIAL_DTR_MASK;
+ ctl_hs |= CP210X_SERIAL_DTR_INACTIVE;
+
+ flow_repl &= ~CP210X_SERIAL_RTS_MASK;
+ flow_repl &= ~CP210X_SERIAL_AUTO_RECEIVE;
+ flow_repl &= ~CP210X_SERIAL_AUTO_TRANSMIT;
+ flow_repl |= CP210X_SERIAL_RTS_INACTIVE;
+
+ flow_repl &= ~CP210X_SERIAL_RTS_MASK;
+ if (enable) {
+ ctl_hs |= CP210X_SERIAL_CTS_HANDSHAKE;
+ } else {
+ ctl_hs &= ~CP210X_SERIAL_CTS_HANDSHAKE;
+ }
+ flow_ctl.lControlHandshake = ctl_hs;
+ flow_ctl.lFlowReplace = flow_repl;
+
+ return usbh_cp210x_control_out(serial, CP210X_SET_FLOW, 0, serial->intf, (uint8_t *)&flow_ctl, sizeof(struct cp210x_flow_ctl));
+}
+
+int usbh_cp210x_set_line_coding(struct usbh_serial *serial, struct cdc_line_coding *line_coding)
+{
+ int ret;
+
+ ret = usbh_cp210x_set_baudrate(serial, line_coding->dwDTERate);
+ if (ret < 0) {
+ return ret;
+ }
+ return usbh_cp210x_set_data_format(serial, line_coding->bDataBits, line_coding->bParityType, line_coding->bCharFormat);
+}
+
+int usbh_cp210x_set_line_state(struct usbh_serial *serial, bool dtr, bool rts)
+{
+ struct cp210x_flow_ctl flow_ctl = { 0 };
+ uint32_t flow_repl;
+ uint32_t ctl_hs;
+ uint16_t control = 0;
+ int ret;
+
+ if (!serial || !serial->hport || !serial->priv) {
+ return -USB_ERR_INVAL;
+ }
+
+ if (serial->rtscts) {
+ ret = usbh_cp210x_control_in(serial, CP210X_GET_FLOW, 0, serial->intf, (uint8_t *)&flow_ctl, sizeof(struct cp210x_flow_ctl));
+ if (ret < 0) {
+ return ret;
+ }
+ ctl_hs = flow_ctl.lControlHandshake;
+ flow_repl = flow_ctl.lFlowReplace;
+
+ ctl_hs &= ~CP210X_SERIAL_DTR_MASK;
+ if (dtr)
+ ctl_hs |= CP210X_SERIAL_DTR_ACTIVE;
+ else
+ ctl_hs |= CP210X_SERIAL_DTR_INACTIVE;
+
+ flow_repl &= ~CP210X_SERIAL_RTS_MASK;
+ if (rts)
+ flow_repl |= CP210X_SERIAL_RTS_FLOW_CTL;
+ else
+ flow_repl |= CP210X_SERIAL_RTS_INACTIVE;
+
+ flow_ctl.lControlHandshake = ctl_hs;
+ flow_ctl.lFlowReplace = flow_repl;
+
+ return usbh_cp210x_control_out(serial, CP210X_SET_FLOW, 0, serial->intf, (uint8_t *)&flow_ctl, sizeof(struct cp210x_flow_ctl));
+ } else {
+ if (dtr) {
+ control |= CP210X_CONTROL_DTR;
+ }
+ if (rts) {
+ control |= CP210X_CONTROL_RTS;
+ }
+ control |= CP210X_CONTROL_WRITE_DTR;
+ control |= CP210X_CONTROL_WRITE_RTS;
+ return usbh_cp210x_control_out(serial, CP210X_SET_MHS, control, serial->intf, NULL, 0);
+ }
+}
+
+static int usbh_cp210x_get_modem_status(struct usbh_serial *serial)
+{
+ int ret;
+ uint8_t control;
+ uint16_t status;
+
+ if (!serial || !serial->hport) {
+ return -USB_ERR_INVAL;
+ }
+
+ ret = usbh_cp210x_control_in(serial, CP210X_GET_MDMSTS, 0, serial->intf, (uint8_t *)&control, 1);
+ if (ret < 0) {
+ return ret;
+ }
+
+ status = ((control & CP210X_CONTROL_DTR) ? USBH_SERIAL_TIOCM_DTR : 0) |
+ ((control & CP210X_CONTROL_RTS) ? USBH_SERIAL_TIOCM_RTS : 0) |
+ ((control & CP210X_CONTROL_CTS) ? USBH_SERIAL_TIOCM_CTS : 0) |
+ ((control & CP210X_CONTROL_DSR) ? USBH_SERIAL_TIOCM_DSR : 0) |
+ ((control & CP210X_CONTROL_RING) ? USBH_SERIAL_TIOCM_RI : 0) |
+ ((control & CP210X_CONTROL_DCD) ? USBH_SERIAL_TIOCM_CD : 0);
+
+ return status;
+}
+
+static const struct usbh_serial_driver cp210x_driver = {
+ .driver_name = "cp210x",
+
+ .ignore_rx_header = 0,
+ .ignore_tx_header = 0,
+
+ .attach = usbh_cp210x_attach,
+ .detach = usbh_cp210x_detach,
+ .set_flow_control = usbh_cp210x_set_flow_ctrl,
+ .set_line_coding = usbh_cp210x_set_line_coding,
+ .get_line_coding = NULL,
+ .set_line_state = usbh_cp210x_set_line_state,
+ .get_modem_status = usbh_cp210x_get_modem_status,
+};
+
+static int usbh_cp210x_connect(struct usbh_hubport *hport, uint8_t intf)
+{
+ return usbh_serial_probe(hport, intf, &cp210x_driver) ? 0 : -USB_ERR_NOMEM;
+}
+
+static int usbh_cp210x_disconnect(struct usbh_hubport *hport, uint8_t intf)
+{
+ struct usbh_serial *serial = (struct usbh_serial *)hport->config.intf[intf].priv;
+
+ if (serial) {
+ usbh_serial_remove(serial);
+ }
+ return 0;
+}
+
+static const uint16_t cp210x_id_table[][2] = {
+ { 0x10C4, 0xEA60 },
+ { 0, 0 },
+};
+
+const struct usbh_class_driver cp210x_class_driver = {
+ .driver_name = "cp210x",
+ .connect = usbh_cp210x_connect,
+ .disconnect = usbh_cp210x_disconnect
+};
+
+CLASS_INFO_DEFINE const struct usbh_class_info cp210x_class_info = {
+ .match_flags = USB_CLASS_MATCH_VID_PID | USB_CLASS_MATCH_INTF_CLASS,
+ .bInterfaceClass = 0xff,
+ .bInterfaceSubClass = 0x00,
+ .bInterfaceProtocol = 0x00,
+ .id_table = cp210x_id_table,
+ .class_driver = &cp210x_class_driver
+}; \ No newline at end of file
diff --git a/class/serial/usbh_cp210x.h b/class/serial/usbh_cp210x.h
new file mode 100644
index 00000000..6bd02067
--- /dev/null
+++ b/class/serial/usbh_cp210x.h
@@ -0,0 +1,187 @@
+/*
+ * Copyright (c) 2024 ~ 2025, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#ifndef USBH_CP210X_H
+#define USBH_CP210X_H
+
+#include "usb_cdc.h"
+
+/* Requests */
+#define CP210X_IFC_ENABLE 0x00
+#define CP210X_SET_BAUDDIV 0x01
+#define CP210X_GET_BAUDDIV 0x02
+#define CP210X_SET_LINE_CTL 0x03 // Set parity, data bits, stop bits
+#define CP210X_GET_LINE_CTL 0x04
+#define CP210X_SET_BREAK 0x05
+#define CP210X_IMM_CHAR 0x06
+#define CP210X_SET_MHS 0x07 // Set DTR, RTS
+#define CP210X_GET_MDMSTS 0x08
+#define CP210X_SET_XON 0x09
+#define CP210X_SET_XOFF 0x0A
+#define CP210X_SET_EVENTMASK 0x0B
+#define CP210X_GET_EVENTMASK 0x0C
+#define CP210X_SET_CHAR 0x0D
+#define CP210X_GET_CHARS 0x0E
+#define CP210X_GET_PROPS 0x0F
+#define CP210X_GET_COMM_STATUS 0x10
+#define CP210X_RESET 0x11
+#define CP210X_PURGE 0x12
+#define CP210X_SET_FLOW 0x13
+#define CP210X_GET_FLOW 0x14
+#define CP210X_EMBED_EVENTS 0x15
+#define CP210X_GET_EVENTSTATE 0x16
+#define CP210X_SET_CHARS 0x19
+#define CP210X_GET_BAUDRATE 0x1D
+#define CP210X_SET_BAUDRATE 0x1E // Set baudrate
+#define CP210X_VENDOR_SPECIFIC 0xFF
+
+/* CP210X_VENDOR_SPECIFIC values */
+#define CP210X_GET_FW_VER 0x000E
+#define CP210X_READ_2NCONFIG 0x000E
+#define CP210X_GET_FW_VER_2N 0x0010
+#define CP210X_READ_LATCH 0x00C2
+#define CP210X_GET_PARTNUM 0x370B
+#define CP210X_GET_PORTCONFIG 0x370C
+#define CP210X_GET_DEVICEMODE 0x3711
+#define CP210X_WRITE_LATCH 0x37E1
+
+/* CP210X_IFC_ENABLE */
+#define CP210X_UART_ENABLE 0x0001
+#define CP210X_UART_DISABLE 0x0000
+
+/* CP210X_(SET|GET)_BAUDDIV */
+#define CP210X_BAUD_RATE_GEN_FREQ 0x384000
+
+/* CP210X_(SET|GET)_LINE_CTL */
+#define CP210X_BITS_DATA_MASK 0X0f00
+#define CP210X_BITS_DATA_5 0X0500
+#define CP210X_BITS_DATA_6 0X0600
+#define CP210X_BITS_DATA_7 0X0700
+#define CP210X_BITS_DATA_8 0X0800
+#define CP210X_BITS_DATA_9 0X0900
+
+#define CP210X_BITS_PARITY_MASK 0x00f0
+#define CP210X_BITS_PARITY_NONE 0x0000
+#define CP210X_BITS_PARITY_ODD 0x0010
+#define CP210X_BITS_PARITY_EVEN 0x0020
+#define CP210X_BITS_PARITY_MARK 0x0030
+#define CP210X_BITS_PARITY_SPACE 0x0040
+
+#define CP210X_BITS_STOP_MASK 0x000f
+#define CP210X_BITS_STOP_1 0x0000
+#define CP210X_BITS_STOP_1_5 0x0001
+#define CP210X_BITS_STOP_2 0x0002
+
+/* CP210X_SET_BREAK */
+#define CP210X_BREAK_ON 0x0001
+#define CP210X_BREAK_OFF 0x0000
+
+/* CP210X_(SET_MHS|GET_MDMSTS) */
+#define CP210X_CONTROL_DTR 0x0001
+#define CP210X_CONTROL_RTS 0x0002
+#define CP210X_CONTROL_CTS 0x0010
+#define CP210X_CONTROL_DSR 0x0020
+#define CP210X_CONTROL_RING 0x0040
+#define CP210X_CONTROL_DCD 0x0080
+#define CP210X_CONTROL_WRITE_DTR 0x0100
+#define CP210X_CONTROL_WRITE_RTS 0x0200
+
+/* CP210X_(GET|SET)_CHARS */
+struct cp210x_special_chars {
+ uint8_t bEofChar;
+ uint8_t bErrorChar;
+ uint8_t bBreakChar;
+ uint8_t bEventChar;
+ uint8_t bXonChar;
+ uint8_t bXoffChar;
+};
+
+/* CP210X_GET_COMM_STATUS returns these 0x13 bytes */
+struct cp210x_comm_status {
+ uint32_t ulErrors;
+ uint32_t ulHoldReasons;
+ uint32_t ulAmountInInQueue;
+ uint32_t ulAmountInOutQueue;
+ uint8_t bEofReceived;
+ uint8_t bWaitForImmediate;
+ uint8_t bReserved;
+} __PACKED;
+
+/*
+ * CP210X_PURGE - 16 bits passed in wValue of USB request.
+ * SiLabs app note AN571 gives a strange description of the 4 bits:
+ * bit 0 or bit 2 clears the transmit queue and 1 or 3 receive.
+ * writing 1 to all, however, purges cp2108 well enough to avoid the hang.
+ */
+#define PURGE_ALL 0x000f
+
+/* CP210X_EMBED_EVENTS */
+#define CP210X_ESCCHAR 0xec
+
+#define CP210X_LSR_OVERRUN BIT(1)
+#define CP210X_LSR_PARITY BIT(2)
+#define CP210X_LSR_FRAME BIT(3)
+#define CP210X_LSR_BREAK BIT(4)
+
+/* CP210X_GET_FLOW/CP210X_SET_FLOW read/write these 0x10 bytes */
+struct cp210x_flow_ctl {
+ uint32_t lControlHandshake;
+ uint32_t lFlowReplace;
+ uint32_t lXonLimit;
+ uint32_t lXoffLimit;
+};
+
+/* cp210x_flow_ctl::ulControlHandshake */
+#define CP210X_SERIAL_DTR_MASK (0x03 << 0)
+#define CP210X_SERIAL_DTR_INACTIVE (0 << 0)
+#define CP210X_SERIAL_DTR_ACTIVE (1 << 0)
+#define CP210X_SERIAL_DTR_FLOW_CTL (2 << 0)
+#define CP210X_SERIAL_CTS_HANDSHAKE BIT(3)
+#define CP210X_SERIAL_DSR_HANDSHAKE BIT(4)
+#define CP210X_SERIAL_DCD_HANDSHAKE BIT(5)
+#define CP210X_SERIAL_DSR_SENSITIVITY BIT(6)
+
+/* cp210x_flow_ctl::ulFlowReplace */
+#define CP210X_SERIAL_AUTO_TRANSMIT BIT(0)
+#define CP210X_SERIAL_AUTO_RECEIVE BIT(1)
+#define CP210X_SERIAL_ERROR_CHAR BIT(2)
+#define CP210X_SERIAL_NULL_STRIPPING BIT(3)
+#define CP210X_SERIAL_BREAK_CHAR BIT(4)
+#define CP210X_SERIAL_RTS_MASK (0x03 << 6)
+#define CP210X_SERIAL_RTS_INACTIVE (0 << 6)
+#define CP210X_SERIAL_RTS_ACTIVE (1 << 6)
+#define CP210X_SERIAL_RTS_FLOW_CTL (2 << 6)
+#define CP210X_SERIAL_XOFF_CONTINUE BIT(31)
+
+/* CP210X_VENDOR_SPECIFIC, CP210X_GET_DEVICEMODE call reads these 0x2 bytes. */
+struct cp210x_pin_mode {
+ uint8_t eci;
+ uint8_t sci;
+};
+
+#define CP210X_PIN_MODE_MODEM 0
+#define CP210X_PIN_MODE_GPIO BIT(0)
+
+/* Part number definitions */
+#define CP210X_PARTNUM_CP2101 0x01
+#define CP210X_PARTNUM_CP2102 0x02
+#define CP210X_PARTNUM_CP2103 0x03
+#define CP210X_PARTNUM_CP2104 0x04
+#define CP210X_PARTNUM_CP2105 0x05
+#define CP210X_PARTNUM_CP2108 0x08
+#define CP210X_PARTNUM_CP2102N_QFN28 0x20
+#define CP210X_PARTNUM_CP2102N_QFN24 0x21
+#define CP210X_PARTNUM_CP2102N_QFN20 0x22
+#define CP210X_PARTNUM_UNKNOWN 0xFF
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* USBH_CP210X_H */
diff --git a/class/serial/usbh_ftdi.c b/class/serial/usbh_ftdi.c
new file mode 100644
index 00000000..7e14c95c
--- /dev/null
+++ b/class/serial/usbh_ftdi.c
@@ -0,0 +1,407 @@
+/*
+ * Copyright (c) 2024 ~ 2025, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#include "usbh_core.h"
+#include "usbh_serial.h"
+#include "usbh_ftdi.h"
+
+#undef USB_DBG_TAG
+#define USB_DBG_TAG "usbh_ftdi"
+#include "usb_log.h"
+
+enum ftdi_chip_type {
+ SIO,
+ FT232A,
+ FT232B,
+ FT2232C,
+ FT232R,
+ FT232H,
+ FT2232H,
+ FT4232H,
+ FT4232HA,
+ FT232HP,
+ FT233HP,
+ FT2232HP,
+ FT2233HP,
+ FT4232HP,
+ FT4233HP,
+ FTX,
+};
+
+static const char *ftdi_chip_name[] = {
+ [SIO] = "SIO", /* the serial part of FT8U100AX */
+ [FT232A] = "FT232A",
+ [FT232B] = "FT232B",
+ [FT2232C] = "FT2232C/D",
+ [FT232R] = "FT232R",
+ [FT232H] = "FT232H",
+ [FT2232H] = "FT2232H",
+ [FT4232H] = "FT4232H",
+ [FT4232HA] = "FT4232HA",
+ [FT232HP] = "FT232HP",
+ [FT233HP] = "FT233HP",
+ [FT2232HP] = "FT2232HP",
+ [FT2233HP] = "FT2233HP",
+ [FT4232HP] = "FT4232HP",
+ [FT4233HP] = "FT4233HP",
+ [FTX] = "FT-X",
+};
+
+struct usbh_ftdi {
+ enum ftdi_chip_type chip_type;
+};
+
+static uint32_t ftdi_232bm_baud_base_to_divisor(uint32_t baud, int base)
+{
+ static const unsigned char divfrac[8] = { 0, 3, 2, 4, 1, 5, 6, 7 };
+ uint32_t divisor;
+ /* divisor shifted 3 bits to the left */
+ int divisor3 = DIV_ROUND_CLOSEST(base, 2 * baud);
+ divisor = divisor3 >> 3;
+ divisor |= (uint32_t)divfrac[divisor3 & 0x7] << 14;
+ /* Deal with special cases for highest baud rates. */
+ if (divisor == 1) /* 1.0 */
+ divisor = 0;
+ else if (divisor == 0x4001) /* 1.5 */
+ divisor = 1;
+ return divisor;
+}
+
+static uint32_t ftdi_232bm_baud_to_divisor(uint32_t baud)
+{
+ return ftdi_232bm_baud_base_to_divisor(baud, 48000000);
+}
+
+static uint32_t ftdi_2232h_baud_base_to_divisor(uint32_t baud, int base)
+{
+ static const unsigned char divfrac[8] = { 0, 3, 2, 4, 1, 5, 6, 7 };
+ uint32_t divisor;
+ int divisor3;
+
+ /* hi-speed baud rate is 10-bit sampling instead of 16-bit */
+ divisor3 = DIV_ROUND_CLOSEST(8 * base, 10 * baud);
+
+ divisor = divisor3 >> 3;
+ divisor |= (uint32_t)divfrac[divisor3 & 0x7] << 14;
+ /* Deal with special cases for highest baud rates. */
+ if (divisor == 1) /* 1.0 */
+ divisor = 0;
+ else if (divisor == 0x4001) /* 1.5 */
+ divisor = 1;
+ /*
+ * Set this bit to turn off a divide by 2.5 on baud rate generator
+ * This enables baud rates up to 12Mbaud but cannot reach below 1200
+ * baud with this bit set
+ */
+ divisor |= 0x00020000;
+ return divisor;
+}
+
+static uint32_t ftdi_2232h_baud_to_divisor(uint32_t baud)
+{
+ return ftdi_2232h_baud_base_to_divisor(baud, 120000000);
+}
+
+int usbh_ftdi_reset(struct usbh_serial *serial)
+{
+ struct usb_setup_packet *setup;
+
+ if (!serial || !serial->hport) {
+ return -USB_ERR_INVAL;
+ }
+ setup = serial->hport->setup;
+
+ setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE;
+ setup->bRequest = FTDI_SIO_RESET;
+ setup->wValue = 0;
+ setup->wIndex = serial->intf;
+ setup->wLength = 0;
+
+ return usbh_control_transfer(serial->hport, setup, NULL);
+}
+
+static int usbh_ftdi_set_baudrate(struct usbh_serial *serial, uint32_t baudrate)
+{
+ struct usb_setup_packet *setup;
+ struct usbh_ftdi *ftdi_class;
+ uint32_t div_value;
+ uint16_t value;
+ uint8_t baudrate_high;
+
+ if (!serial || !serial->hport || !serial->priv) {
+ return -USB_ERR_INVAL;
+ }
+ setup = serial->hport->setup;
+ ftdi_class = (struct usbh_ftdi *)serial->priv;
+
+ switch (ftdi_class->chip_type) {
+ case FT232B:
+ case FT2232C:
+ case FT232R:
+ if (baudrate > 3000000) {
+ return -USB_ERR_INVAL;
+ }
+ div_value = ftdi_232bm_baud_to_divisor(baudrate);
+ break;
+ default:
+ if ((baudrate <= 12000000) && (baudrate >= 1200)) {
+ div_value = ftdi_2232h_baud_to_divisor(baudrate);
+ } else if (baudrate < 1200) {
+ div_value = ftdi_232bm_baud_to_divisor(baudrate);
+ } else {
+ return -USB_ERR_INVAL;
+ }
+ break;
+ }
+
+ value = div_value & 0xFFFF;
+ baudrate_high = (div_value >> 16) & 0xff;
+
+ setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE;
+ setup->bRequest = FTDI_SIO_SET_BAUDRATE;
+ setup->wValue = value;
+ setup->wIndex = (baudrate_high << 8) | serial->intf;
+ setup->wLength = 0;
+
+ return usbh_control_transfer(serial->hport, setup, NULL);
+}
+
+static int usbh_ftdi_set_data_format(struct usbh_serial *serial, uint8_t databits, uint8_t parity, uint8_t stopbits, uint8_t isbreak)
+{
+ /**
+ * D0-D7 databits BITS_7=7, BITS_8=8
+ * D8-D10 parity NONE=0, ODD=1, EVEN=2, MARK=3, SPACE=4
+ * D11-D12 STOP_BIT_1=0, STOP_BIT_15=1, STOP_BIT_2=2
+ * D14 BREAK_OFF=0, BREAK_ON=1
+ **/
+ struct usb_setup_packet *setup;
+ uint16_t value;
+
+ if (!serial || !serial->hport) {
+ return -USB_ERR_INVAL;
+ }
+ setup = serial->hport->setup;
+
+ value = ((isbreak & 0x01) << 14) | ((stopbits & 0x03) << 11) | ((parity & 0x0f) << 8) | (databits & 0x0f);
+
+ setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE;
+ setup->bRequest = FTDI_SIO_SET_DATA;
+ setup->wValue = value;
+ setup->wIndex = serial->intf;
+ setup->wLength = 0;
+
+ return usbh_control_transfer(serial->hport, setup, NULL);
+}
+
+static int usbh_ftdi_set_latency_timer(struct usbh_serial *serial, uint16_t value)
+{
+ struct usb_setup_packet *setup;
+
+ if (!serial || !serial->hport) {
+ return -USB_ERR_INVAL;
+ }
+ setup = serial->hport->setup;
+
+ setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE;
+ setup->bRequest = FTDI_SIO_SET_LATENCY_TIMER;
+ setup->wValue = value;
+ setup->wIndex = serial->intf;
+ setup->wLength = 0;
+
+ return usbh_control_transfer(serial->hport, setup, NULL);
+}
+
+static int usbh_ftdi_attach(struct usbh_serial *serial)
+{
+ uint16_t version;
+ uint8_t chip_type;
+ int ret;
+
+ version = serial->hport->device_desc.bcdDevice;
+
+ switch (version) {
+ case 0x400:
+ chip_type = FT232B;
+ break;
+ case 0x500:
+ chip_type = FT2232C;
+ break;
+ case 0x600:
+ chip_type = FT232R;
+ break;
+ case 0x700:
+ chip_type = FT2232H;
+ break;
+ case 0x900:
+ chip_type = FT232H;
+ break;
+
+ default:
+ USB_LOG_ERR("Unsupported FTDI chip version: 0x%04x\r\n", version);
+ return -USB_ERR_NOTSUPP;
+ }
+
+ USB_LOG_INFO("chip name: %s\r\n", ftdi_chip_name[chip_type]);
+
+ struct usbh_ftdi *ftdi_class = usb_osal_malloc(sizeof(struct usbh_ftdi));
+ if (!ftdi_class) {
+ USB_LOG_ERR("No memory for ftdi_class\r\n");
+ return -USB_ERR_NOMEM;
+ }
+ memset(ftdi_class, 0, sizeof(struct usbh_ftdi));
+ serial->priv = ftdi_class;
+
+ ftdi_class->chip_type = chip_type;
+ ret = usbh_ftdi_set_latency_timer(serial, 0x10);
+ if (ret < 0) {
+ goto errout;
+ }
+ return 0;
+errout:
+ serial->priv = NULL;
+ usb_osal_free(ftdi_class);
+ return ret;
+}
+
+static void usbh_ftdi_detach(struct usbh_serial *serial)
+{
+ if (serial && serial->priv) {
+ serial->priv = NULL;
+ usb_osal_free(serial->priv);
+ }
+}
+
+static int usbh_ftdi_set_flow_ctrl(struct usbh_serial *serial, bool hardctrl)
+{
+ struct usb_setup_packet *setup;
+
+ if (!serial || !serial->hport) {
+ return -USB_ERR_INVAL;
+ }
+ setup = serial->hport->setup;
+
+ setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE;
+ setup->bRequest = FTDI_SIO_SET_FLOW_CTRL;
+ setup->wValue = hardctrl ? FTDI_SIO_RTS_CTS_HS : FTDI_SIO_DISABLE_FLOW_CTRL;
+ setup->wIndex = serial->intf;
+ setup->wLength = 0;
+
+ return usbh_control_transfer(serial->hport, setup, NULL);
+}
+
+static int usbh_ftdi_set_line_coding(struct usbh_serial *serial, struct cdc_line_coding *line_coding)
+{
+ int ret = usbh_ftdi_set_baudrate(serial, line_coding->dwDTERate);
+ if (ret < 0) {
+ return ret;
+ }
+ return usbh_ftdi_set_data_format(serial, line_coding->bDataBits, line_coding->bParityType, line_coding->bCharFormat, 0);
+}
+
+static int usbh_ftdi_set_line_state(struct usbh_serial *serial, bool dtr, bool rts)
+{
+ struct usb_setup_packet *setup;
+ uint16_t value = 0;
+
+ if (!serial || !serial->hport) {
+ return -USB_ERR_INVAL;
+ }
+ setup = serial->hport->setup;
+
+ value = ((dtr ? FTDI_SIO_SET_DTR_HIGH : FTDI_SIO_SET_DTR_LOW) | (rts ? FTDI_SIO_SET_RTS_HIGH : FTDI_SIO_SET_RTS_LOW));
+
+ setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE;
+ setup->bRequest = FTDI_SIO_SET_MODEM_CTRL;
+ setup->wValue = value;
+ setup->wIndex = serial->intf;
+ setup->wLength = 0;
+
+ return usbh_control_transfer(serial->hport, setup, NULL);
+}
+
+static int usbh_ftdi_get_modem_status(struct usbh_serial *serial)
+{
+ struct usb_setup_packet *setup;
+ uint16_t status = 0;
+ int ret;
+
+ if (!serial || !serial->hport) {
+ return -USB_ERR_INVAL;
+ }
+
+ setup = serial->hport->setup;
+
+ setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE;
+ setup->bRequest = FTDI_SIO_GET_MODEM_STATUS;
+ setup->wValue = 0x0000;
+ setup->wIndex = serial->intf;
+ setup->wLength = 2;
+
+ ret = usbh_control_transfer(serial->hport, setup, serial->iobuffer);
+ if (ret < 0) {
+ return 0;
+ }
+
+ status = (serial->iobuffer[0] & FTDI_SIO_DSR_MASK ? USBH_SERIAL_TIOCM_DSR : 0) |
+ (serial->iobuffer[0] & FTDI_SIO_CTS_MASK ? USBH_SERIAL_TIOCM_CTS : 0) |
+ (serial->iobuffer[0] & FTDI_SIO_RI_MASK ? USBH_SERIAL_TIOCM_RI : 0) |
+ (serial->iobuffer[0] & FTDI_SIO_RLSD_MASK ? USBH_SERIAL_TIOCM_CD : 0) |
+ (serial->line_state & USBH_SERIAL_TIOCM_DTR ? USBH_SERIAL_TIOCM_DTR : 0) |
+ (serial->line_state & USBH_SERIAL_TIOCM_RTS ? USBH_SERIAL_TIOCM_RTS : 0);
+
+ return status;
+}
+
+static const struct usbh_serial_driver ftdi_driver = {
+ .driver_name = "ftdi",
+
+ .ignore_rx_header = 2,
+ .ignore_tx_header = 0,
+
+ .attach = usbh_ftdi_attach,
+ .detach = usbh_ftdi_detach,
+ .set_flow_control = usbh_ftdi_set_flow_ctrl,
+ .set_line_coding = usbh_ftdi_set_line_coding,
+ .get_line_coding = NULL,
+ .set_line_state = usbh_ftdi_set_line_state,
+ .get_modem_status = usbh_ftdi_get_modem_status,
+};
+
+static int usbh_ftdi_connect(struct usbh_hubport *hport, uint8_t intf)
+{
+ return usbh_serial_probe(hport, intf, &ftdi_driver) ? 0 : -USB_ERR_NOMEM;
+}
+
+static int usbh_ftdi_disconnect(struct usbh_hubport *hport, uint8_t intf)
+{
+ struct usbh_serial *serial = (struct usbh_serial *)hport->config.intf[intf].priv;
+
+ if (serial) {
+ usbh_serial_remove(serial);
+ }
+ return 0;
+}
+
+static const uint16_t ftdi_id_table[][2] = {
+ { 0x0403, 0x6001 },
+ { 0x0403, 0x6010 },
+ { 0x0403, 0x6014 },
+ { 0, 0 },
+};
+
+const struct usbh_class_driver ftdi_class_driver = {
+ .driver_name = "ftdi",
+ .connect = usbh_ftdi_connect,
+ .disconnect = usbh_ftdi_disconnect
+};
+
+CLASS_INFO_DEFINE const struct usbh_class_info ftdi_class_info = {
+ .match_flags = USB_CLASS_MATCH_VID_PID | USB_CLASS_MATCH_INTF_CLASS,
+ .bInterfaceClass = 0xff,
+ .bInterfaceSubClass = 0x00,
+ .bInterfaceProtocol = 0x00,
+ .id_table = ftdi_id_table,
+ .class_driver = &ftdi_class_driver
+}; \ No newline at end of file
diff --git a/class/serial/usbh_ftdi.h b/class/serial/usbh_ftdi.h
new file mode 100644
index 00000000..0d176299
--- /dev/null
+++ b/class/serial/usbh_ftdi.h
@@ -0,0 +1,341 @@
+/*
+ * Copyright (c) 2024 ~ 2025, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#ifndef USBH_FTDI_H
+#define USBH_FTDI_H
+
+#include "usb_cdc.h"
+
+#define FTDI_VID 0x0403 /* Vendor Id */
+
+/* FTDI device PIDs */
+#define FTDI_8U232AM_PID 0x6001 /* Similar device to SIO above */
+#define FTDI_8U232AM_ALT_PID 0x6006 /* FTDI's alternate PID for above */
+#define FTDI_8U2232C_PID 0x6010 /* Dual channel device */
+#define FTDI_4232H_PID 0x6011 /* Quad channel hi-speed device */
+#define FTDI_232H_PID 0x6014 /* Single channel hi-speed device */
+#define FTDI_FTX_PID 0x6015 /* FT-X series (FT201X, FT230X, FT231X, etc) */
+#define FTDI_FT2233HP_PID 0x6040 /* Dual channel hi-speed device with PD */
+#define FTDI_FT4233HP_PID 0x6041 /* Quad channel hi-speed device with PD */
+#define FTDI_FT2232HP_PID 0x6042 /* Dual channel hi-speed device with PD */
+#define FTDI_FT4232HP_PID 0x6043 /* Quad channel hi-speed device with PD */
+#define FTDI_FT233HP_PID 0x6044 /* Dual channel hi-speed device with PD */
+#define FTDI_FT232HP_PID 0x6045 /* Dual channel hi-speed device with PD */
+#define FTDI_FT4232HA_PID 0x6048 /* Quad channel automotive grade hi-speed device */
+#define FTDI_SIO_PID 0x8372 /* Product Id SIO application of 8U100AX */
+#define FTDI_232RL_PID 0xFBFA /* Product ID for FT232RL */
+
+/* Requests */
+#define FTDI_SIO_RESET 0x00 /* Reset the port */
+#define FTDI_SIO_SET_MODEM_CTRL 0x01 /* Set the modem control register */
+#define FTDI_SIO_SET_FLOW_CTRL 0x02 /* Set flow control register */
+#define FTDI_SIO_SET_BAUDRATE 0x03 /* Set baud rate */
+#define FTDI_SIO_SET_DATA 0x04 /* Set the data characteristics of the port */
+#define FTDI_SIO_GET_MODEM_STATUS 0x05
+#define FTDI_SIO_SET_EVENT_CHAR 0x06
+#define FTDI_SIO_SET_ERROR_CHAR 0x07
+#define FTDI_SIO_SET_LATENCY_TIMER 0x09
+#define FTDI_SIO_GET_LATENCY_TIMER 0x0A
+#define FTDI_SIO_SET_BITMODE 0x0B
+#define FTDI_SIO_READ_PINS 0x0C
+#define FTDI_SIO_READ_EEPROM 0x90
+#define FTDI_SIO_WRITE_EEPROM 0x91
+#define FTDI_SIO_ERASE_EEPROM 0x92
+
+/* Channel indices for FT2232, FT2232H and FT4232H devices */
+#define FTDI_SIO_CHANNEL_A 1
+#define FTDI_SIO_CHANNEL_B 2
+#define FTDI_SIO_CHANNEL_C 3
+#define FTDI_SIO_CHANNEL_D 4
+
+/*
+ * BmRequestType: 0100 0000B
+ * bRequest: FTDI_SIO_RESET
+ * wValue: Control Value
+ * 0 = Reset SIO
+ * 1 = Purge RX buffer
+ * 2 = Purge TX buffer
+ * wIndex: Port
+ * wLength: 0
+ * Data: None
+ *
+ * The Reset SIO command has this effect:
+ *
+ * Sets flow control set to 'none'
+ * Event char = $0D
+ * Event trigger = disabled
+ * Purge RX buffer
+ * Purge TX buffer
+ * Clear DTR
+ * Clear RTS
+ * baud and data format not reset
+ *
+ * The Purge RX and TX buffer commands affect nothing except the buffers
+ *
+ */
+
+#define FTDI_SIO_RESET_SIO 0
+#define FTDI_SIO_RESET_PURGE_RX 1
+#define FTDI_SIO_RESET_PURGE_TX 2
+
+/*
+ * BmRequestType: 0100 0000B
+ * bRequest: FTDI_SIO_SET_BAUDRATE
+ * wValue: BaudDivisor value - see below
+ * wIndex: Port
+ * wLength: 0
+ * Data: None
+ * The BaudDivisor values are calculated as follows:
+ * - BaseClock is either 12000000 or 48000000 depending on the device.
+ * FIXME: I wish I knew how to detect old chips to select proper base clock!
+ * - BaudDivisor is a fixed point number encoded in a funny way.
+ * (--WRONG WAY OF THINKING--)
+ * BaudDivisor is a fixed point number encoded with following bit weighs:
+ * (-2)(-1)(13..0). It is a radical with a denominator of 4, so values
+ * end with 0.0 (00...), 0.25 (10...), 0.5 (01...), and 0.75 (11...).
+ * (--THE REALITY--)
+ * The both-bits-set has quite different meaning from 0.75 - the chip
+ * designers have decided it to mean 0.125 instead of 0.75.
+ * This info looked up in FTDI application note "FT8U232 DEVICES \ Data Rates
+ * and Flow Control Consideration for USB to RS232".
+ * - BaudDivisor = (BaseClock / 16) / BaudRate, where the (=) operation should
+ * automagically re-encode the resulting value to take fractions into
+ * consideration.
+ * As all values are integers, some bit twiddling is in order:
+ * BaudDivisor = (BaseClock / 16 / BaudRate) |
+ * (((BaseClock / 2 / BaudRate) & 4) ? 0x4000 // 0.5
+ * : ((BaseClock / 2 / BaudRate) & 2) ? 0x8000 // 0.25
+ * : ((BaseClock / 2 / BaudRate) & 1) ? 0xc000 // 0.125
+ * : 0)
+ *
+ * For the FT232BM, a 17th divisor bit was introduced to encode the multiples
+ * of 0.125 missing from the FT8U232AM. Bits 16 to 14 are coded as follows
+ * (the first four codes are the same as for the FT8U232AM, where bit 16 is
+ * always 0):
+ * 000 - add .000 to divisor
+ * 001 - add .500 to divisor
+ * 010 - add .250 to divisor
+ * 011 - add .125 to divisor
+ * 100 - add .375 to divisor
+ * 101 - add .625 to divisor
+ * 110 - add .750 to divisor
+ * 111 - add .875 to divisor
+ * Bits 15 to 0 of the 17-bit divisor are placed in the urb value. Bit 16 is
+ * placed in bit 0 of the urb index.
+ *
+ * Note that there are a couple of special cases to support the highest baud
+ * rates. If the calculated divisor value is 1, this needs to be replaced with
+ * 0. Additionally for the FT232BM, if the calculated divisor value is 0x4001
+ * (1.5), this needs to be replaced with 0x0001 (1) (but this divisor value is
+ * not supported by the FT8U232AM).
+ */
+
+enum ftdi_sio_baudrate {
+ ftdi_sio_b300 = 0,
+ ftdi_sio_b600 = 1,
+ ftdi_sio_b1200 = 2,
+ ftdi_sio_b2400 = 3,
+ ftdi_sio_b4800 = 4,
+ ftdi_sio_b9600 = 5,
+ ftdi_sio_b19200 = 6,
+ ftdi_sio_b38400 = 7,
+ ftdi_sio_b57600 = 8,
+ ftdi_sio_b115200 = 9
+};
+
+/*
+ * BmRequestType: 0100 0000B
+ * bRequest: FTDI_SIO_SET_DATA
+ * wValue: Data characteristics (see below)
+ * wIndex: Port
+ * wLength: 0
+ * Data: No
+ *
+ * Data characteristics
+ *
+ * B0..7 Number of data bits
+ * B8..10 Parity
+ * 0 = None
+ * 1 = Odd
+ * 2 = Even
+ * 3 = Mark
+ * 4 = Space
+ * B11..13 Stop Bits
+ * 0 = 1
+ * 1 = 1.5
+ * 2 = 2
+ * B14
+ * 1 = TX ON (break)
+ * 0 = TX OFF (normal state)
+ * B15 Reserved
+ *
+ */
+
+#define FTDI_SIO_SET_DATA_PARITY_NONE (0x0 << 8)
+#define FTDI_SIO_SET_DATA_PARITY_ODD (0x1 << 8)
+#define FTDI_SIO_SET_DATA_PARITY_EVEN (0x2 << 8)
+#define FTDI_SIO_SET_DATA_PARITY_MARK (0x3 << 8)
+#define FTDI_SIO_SET_DATA_PARITY_SPACE (0x4 << 8)
+#define FTDI_SIO_SET_DATA_STOP_BITS_1 (0x0 << 11)
+#define FTDI_SIO_SET_DATA_STOP_BITS_15 (0x1 << 11)
+#define FTDI_SIO_SET_DATA_STOP_BITS_2 (0x2 << 11)
+#define FTDI_SIO_SET_BREAK (0x1 << 14)
+
+/*
+ * BmRequestType: 0100 0000B
+ * bRequest: FTDI_SIO_MODEM_CTRL
+ * wValue: ControlValue (see below)
+ * wIndex: Port
+ * wLength: 0
+ * Data: None
+ *
+ * NOTE: If the device is in RTS/CTS flow control, the RTS set by this
+ * command will be IGNORED without an error being returned
+ * Also - you can not set DTR and RTS with one control message
+ *
+ * ControlValue
+ * B0 DTR state
+ * 0 = reset
+ * 1 = set
+ * B1 RTS state
+ * 0 = reset
+ * 1 = set
+ * B2..7 Reserved
+ * B8 DTR state enable
+ * 0 = ignore
+ * 1 = use DTR state
+ * B9 RTS state enable
+ * 0 = ignore
+ * 1 = use RTS state
+ * B10..15 Reserved
+ *
+ */
+
+#define FTDI_SIO_SET_DTR_MASK 0x1
+#define FTDI_SIO_SET_DTR_HIGH ((FTDI_SIO_SET_DTR_MASK << 8) | 1)
+#define FTDI_SIO_SET_DTR_LOW ((FTDI_SIO_SET_DTR_MASK << 8) | 0)
+#define FTDI_SIO_SET_RTS_MASK 0x2
+#define FTDI_SIO_SET_RTS_HIGH ((FTDI_SIO_SET_RTS_MASK << 8) | 2)
+#define FTDI_SIO_SET_RTS_LOW ((FTDI_SIO_SET_RTS_MASK << 8) | 0)
+
+/*
+ * BmRequestType: 0100 0000b
+ * bRequest: FTDI_SIO_SET_FLOW_CTRL
+ * wValue: Xoff/Xon
+ * wIndex: Protocol/Port - hIndex is protocol / lIndex is port
+ * wLength: 0
+ * Data: None
+ *
+ * hIndex protocol is:
+ * B0 Output handshaking using RTS/CTS
+ * 0 = disabled
+ * 1 = enabled
+ * B1 Output handshaking using DTR/DSR
+ * 0 = disabled
+ * 1 = enabled
+ * B2 Xon/Xoff handshaking
+ * 0 = disabled
+ * 1 = enabled
+ *
+ * A value of zero in the hIndex field disables handshaking
+ *
+ * If Xon/Xoff handshaking is specified, the hValue field should contain the
+ * XOFF character and the lValue field contains the XON character.
+ */
+
+#define FTDI_SIO_DISABLE_FLOW_CTRL 0x0
+#define FTDI_SIO_RTS_CTS_HS (0x1 << 8)
+#define FTDI_SIO_DTR_DSR_HS (0x2 << 8)
+#define FTDI_SIO_XON_XOFF_HS (0x4 << 8)
+
+/*
+ * BmRequestType: 1100 0000b
+ * bRequest: FTDI_SIO_GET_MODEM_STATUS
+ * wValue: zero
+ * wIndex: Port
+ * wLength: 1
+ * Data: Status
+ *
+ * One byte of data is returned
+ * B0..3 0
+ * B4 CTS
+ * 0 = inactive
+ * 1 = active
+ * B5 DSR
+ * 0 = inactive
+ * 1 = active
+ * B6 Ring Indicator (RI)
+ * 0 = inactive
+ * 1 = active
+ * B7 Receive Line Signal Detect (RLSD)
+ * 0 = inactive
+ * 1 = active
+ */
+
+#define FTDI_SIO_CTS_MASK 0x10
+#define FTDI_SIO_DSR_MASK 0x20
+#define FTDI_SIO_RI_MASK 0x40
+#define FTDI_SIO_RLSD_MASK 0x80
+
+/* Possible bitmodes for FTDI_SIO_SET_BITMODE_REQUEST */
+#define FTDI_SIO_BITMODE_RESET 0x00
+#define FTDI_SIO_BITMODE_CBUS 0x20
+
+/*
+ * IN Endpoint
+ *
+ * The device reserves the first two bytes of data on this endpoint to contain
+ * the current values of the modem and line status registers. In the absence of
+ * data, the device generates a message consisting of these two status bytes
+ * every 40 ms
+ *
+ * Byte 0: Modem Status
+ *
+ * Offset Description
+ * B0 Reserved - must be 1
+ * B1 Reserved - must be 0
+ * B2 Reserved - must be 0
+ * B3 Reserved - must be 0
+ * B4 Clear to Send (CTS)
+ * B5 Data Set Ready (DSR)
+ * B6 Ring Indicator (RI)
+ * B7 Receive Line Signal Detect (RLSD)
+ *
+ * Byte 1: Line Status
+ *
+ * Offset Description
+ * B0 Data Ready (DR)
+ * B1 Overrun Error (OE)
+ * B2 Parity Error (PE)
+ * B3 Framing Error (FE)
+ * B4 Break Interrupt (BI)
+ * B5 Transmitter Holding Register (THRE)
+ * B6 Transmitter Empty (TEMT)
+ * B7 Error in RCVR FIFO
+ *
+ */
+#define FTDI_RS0_CTS (1 << 4)
+#define FTDI_RS0_DSR (1 << 5)
+#define FTDI_RS0_RI (1 << 6)
+#define FTDI_RS0_RLSD (1 << 7)
+
+#define FTDI_RS_DR 1
+#define FTDI_RS_OE (1 << 1)
+#define FTDI_RS_PE (1 << 2)
+#define FTDI_RS_FE (1 << 3)
+#define FTDI_RS_BI (1 << 4)
+#define FTDI_RS_THRE (1 << 5)
+#define FTDI_RS_TEMT (1 << 6)
+#define FTDI_RS_FIFO (1 << 7)
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* USBH_FTDI_H */
diff --git a/class/serial/usbh_gsm.c b/class/serial/usbh_gsm.c
new file mode 100644
index 00000000..096a57de
--- /dev/null
+++ b/class/serial/usbh_gsm.c
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 2025, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#include "usbh_core.h"
+#include "usbh_serial.h"
+
+#undef USB_DBG_TAG
+#define USB_DBG_TAG "usbh_gsm"
+#include "usb_log.h"
+
+struct usbh_gsm {
+ struct usb_endpoint_descriptor *intin;
+ struct usbh_urb intin_urb;
+ struct usb_osal_timer *modem_timer;
+ uint16_t modem_status;
+};
+
+static int usbh_gsm_attach(struct usbh_serial *serial)
+{
+ struct usb_endpoint_descriptor *ep_desc;
+ int ret;
+
+ struct usbh_gsm *gsm_class = usb_osal_malloc(sizeof(struct usbh_gsm));
+ if (!gsm_class) {
+ USB_LOG_ERR("No memory for gsm_class\r\n");
+ return -USB_ERR_NOMEM;
+ }
+ memset(gsm_class, 0, sizeof(struct usbh_gsm));
+ serial->priv = gsm_class;
+
+ for (uint8_t i = 0; i < serial->hport->config.intf[serial->intf].altsetting[0].intf_desc.bNumEndpoints; i++) {
+ ep_desc = &serial->hport->config.intf[serial->intf].altsetting[0].ep[i].ep_desc;
+
+ if (USB_GET_ENDPOINT_TYPE(ep_desc->bmAttributes) == USB_ENDPOINT_TYPE_INTERRUPT) {
+ if (ep_desc->bEndpointAddress & 0x80) {
+ USBH_EP_INIT(gsm_class->intin, ep_desc);
+ break;
+ } else {
+ }
+ }
+ }
+
+ if (!gsm_class->intin) {
+ USB_LOG_ERR("Failed to find interrupt endpoint\r\n");
+ ret = -USB_ERR_NODEV;
+ goto errout;
+ }
+ return 0;
+errout:
+ serial->priv = NULL;
+ usb_osal_free(gsm_class);
+ return ret;
+}
+
+static void usbh_gsm_detach(struct usbh_serial *serial)
+{
+ struct usbh_gsm *gsm_class;
+
+ if (!serial || !serial->priv) {
+ return;
+ }
+
+ gsm_class = (struct usbh_gsm *)serial->priv;
+ if (gsm_class->intin) {
+ usbh_kill_urb(&gsm_class->intin_urb);
+ }
+ serial->priv = NULL;
+ usb_osal_free(gsm_class);
+}
+
+static const struct usbh_serial_driver gsm_driver = {
+ .driver_name = "gsm",
+
+ .ignore_rx_header = 0,
+ .ignore_tx_header = 0,
+
+ .attach = usbh_gsm_attach,
+ .detach = usbh_gsm_detach,
+ .set_flow_control = NULL,
+ .set_line_coding = NULL,
+ .get_line_coding = NULL,
+ .set_line_state = NULL,
+ .get_modem_status = NULL,
+};
+
+static int usbh_gsm_connect(struct usbh_hubport *hport, uint8_t intf)
+{
+ return usbh_serial_probe(hport, intf, &gsm_driver) ? 0 : -USB_ERR_NOMEM;
+}
+
+static int usbh_gsm_disconnect(struct usbh_hubport *hport, uint8_t intf)
+{
+ struct usbh_serial *serial = (struct usbh_serial *)hport->config.intf[intf].priv;
+
+ if (serial) {
+ usbh_serial_remove(serial);
+ }
+ return 0;
+}
+
+const struct usbh_class_driver gsm_class_driver = {
+ .driver_name = "gsm",
+ .connect = usbh_gsm_connect,
+ .disconnect = usbh_gsm_disconnect
+};
+
+static const uint16_t gsm_id_table[][2] = {
+ { 0x2C7C, 0x0120 }, /* Quectel EC20 */
+ { 0x2C7C, 0x0121 }, /* Quectel EC21 */
+ { 0x2C7C, 0x0125 }, /* Quectel EC25 */
+ { 0x2C7C, 0x0191 }, /* Quectel EG91 */
+ { 0x2C7C, 0x0195 }, /* Quectel EG95 */
+ { 0x2C7C, 0x6002 }, /* Quectel EC200/EC600/EC800/EG91x */
+ { 0x1E0E, 0x9001 }, /* SIMCOM SIM7600 */
+ { 0, 0 },
+};
+
+CLASS_INFO_DEFINE const struct usbh_class_info gsm_class_info = {
+ .match_flags = USB_CLASS_MATCH_VID_PID | USB_CLASS_MATCH_INTF_CLASS | USB_CLASS_MATCH_INTF_SUBCLASS | USB_CLASS_MATCH_INTF_PROTOCOL,
+ .bInterfaceClass = 0xff,
+ .bInterfaceSubClass = 0x00,
+ .bInterfaceProtocol = 0x00,
+ .id_table = gsm_id_table,
+ .class_driver = &gsm_class_driver
+};
diff --git a/class/serial/usbh_pl2303.c b/class/serial/usbh_pl2303.c
new file mode 100644
index 00000000..684c5f54
--- /dev/null
+++ b/class/serial/usbh_pl2303.c
@@ -0,0 +1,726 @@
+/*
+ * Copyright (c) 2024 ~ 2025, sakumisu
+ * Copyright (c) 2024, Derek Konigsberg
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#include "usbh_core.h"
+#include "usbh_serial.h"
+#include "usbh_pl2303.h"
+
+#undef USB_DBG_TAG
+#define USB_DBG_TAG "usbh_pl2303"
+#include "usb_log.h"
+
+#define UART_STATE_INDEX 8
+#define UART_STATE_MSR_MASK 0x8b
+#define UART_STATE_TRANSIENT_MASK 0x74
+#define UART_DCD 0x01
+#define UART_DSR 0x02
+#define UART_BREAK_ERROR 0x04
+#define UART_RING 0x08
+#define UART_FRAME_ERROR 0x10
+#define UART_PARITY_ERROR 0x20
+#define UART_OVERRUN_ERROR 0x40
+#define UART_CTS 0x80
+
+struct pl2303_type_data {
+ const char *name;
+ uint32_t max_baud_rate;
+ unsigned long quirks;
+ unsigned int no_autoxonxoff : 1;
+ unsigned int no_divisors : 1;
+ unsigned int alt_divisors : 1;
+};
+
+enum pl2303_type {
+ TYPE_H,
+ TYPE_HX,
+ TYPE_TA,
+ TYPE_TB,
+ TYPE_HXD,
+ TYPE_HXN,
+ TYPE_COUNT
+};
+
+struct usbh_pl2303 {
+ enum pl2303_type chip_type;
+ uint32_t quirks;
+ struct usb_endpoint_descriptor *intin;
+ struct usbh_urb intin_urb;
+ struct usb_osal_timer *modem_timer;
+ uint16_t modem_status;
+};
+
+static const struct pl2303_type_data pl2303_type_data[TYPE_COUNT] = {
+ [TYPE_H] = {
+ .name = "PL2303H",
+ .max_baud_rate = 1228800,
+ .quirks = PL2303_QUIRK_LEGACY,
+ .no_autoxonxoff = true,
+ },
+ [TYPE_HX] = {
+ .name = "PL2303HX",
+ .max_baud_rate = 6000000,
+ },
+ [TYPE_TA] = {
+ .name = "PL2303TA",
+ .max_baud_rate = 6000000,
+ .alt_divisors = true,
+ },
+ [TYPE_TB] = {
+ .name = "PL2303TB",
+ .max_baud_rate = 12000000,
+ .alt_divisors = true,
+ },
+ [TYPE_HXD] = {
+ .name = "PL2303HXD",
+ .max_baud_rate = 12000000,
+ },
+ [TYPE_HXN] = {
+ .name = "PL2303G",
+ .max_baud_rate = 12000000,
+ .no_divisors = true,
+ },
+};
+
+/*
+ * Returns the nearest supported baud rate that can be set directly without
+ * using divisors.
+ */
+static uint32_t pl2303_get_supported_baud_rate(uint32_t baud)
+{
+ static const uint32_t baud_sup[] = {
+ 75, 150, 300, 600, 1200, 1800, 2400, 3600, 4800, 7200, 9600,
+ 14400, 19200, 28800, 38400, 57600, 115200, 230400, 460800,
+ 614400, 921600, 1228800, 2457600, 3000000, 6000000
+ };
+
+ unsigned i;
+
+ for (i = 0; i < ARRAY_SIZE(baud_sup); ++i) {
+ if (baud_sup[i] > baud)
+ break;
+ }
+
+ if (i == ARRAY_SIZE(baud_sup))
+ baud = baud_sup[i - 1];
+ else if (i > 0 && (baud_sup[i] - baud) > (baud - baud_sup[i - 1]))
+ baud = baud_sup[i - 1];
+ else
+ baud = baud_sup[i];
+
+ return baud;
+}
+
+/*
+ * NOTE: If unsupported baud rates are set directly, the PL2303 seems to
+ * use 9600 baud.
+ */
+static uint32_t pl2303_encode_baud_rate_direct(unsigned char buf[4],
+ uint32_t baud)
+{
+ memcpy(buf, &baud, 4);
+
+ return baud;
+}
+
+static uint32_t pl2303_encode_baud_rate_divisor_alt(unsigned char buf[4],
+ uint32_t baud)
+{
+ unsigned int baseline, mantissa, exponent;
+
+ /*
+ * Apparently, for the TA version the formula is:
+ * baudrate = 12M * 32 / (mantissa * 2^exponent)
+ * where
+ * mantissa = buf[10:0]
+ * exponent = buf[15:13 16]
+ */
+ baseline = 12000000 * 32;
+ mantissa = baseline / baud;
+ if (mantissa == 0)
+ mantissa = 1; /* Avoid dividing by zero if baud > 32*12M. */
+ exponent = 0;
+ while (mantissa >= 2048) {
+ if (exponent < 15) {
+ mantissa >>= 1; /* divide by 2 */
+ exponent++;
+ } else {
+ /* Exponent is maxed. Trim mantissa and leave. */
+ mantissa = 2047;
+ break;
+ }
+ }
+
+ buf[3] = 0x80;
+ buf[2] = exponent & 0x01;
+ buf[1] = (exponent & ~0x01) << 4 | mantissa >> 8;
+ buf[0] = mantissa & 0xff;
+
+ /* Calculate and return the exact baud rate. */
+ baud = (baseline / mantissa) >> exponent;
+
+ return baud;
+}
+
+static uint32_t pl2303_encode_baud_rate_divisor(unsigned char buf[4],
+ uint32_t baud)
+{
+ unsigned int baseline, mantissa, exponent;
+
+ /*
+ * Apparently the formula is:
+ * baudrate = 12M * 32 / (mantissa * 4^exponent)
+ * where
+ * mantissa = buf[8:0]
+ * exponent = buf[11:9]
+ */
+ baseline = 12000000 * 32;
+ mantissa = baseline / baud;
+ if (mantissa == 0)
+ mantissa = 1; /* Avoid dividing by zero if baud > 32*12M. */
+ exponent = 0;
+ while (mantissa >= 512) {
+ if (exponent < 7) {
+ mantissa >>= 2; /* divide by 4 */
+ exponent++;
+ } else {
+ /* Exponent is maxed. Trim mantissa and leave. */
+ mantissa = 511;
+ break;
+ }
+ }
+
+ buf[3] = 0x80;
+ buf[2] = 0;
+ buf[1] = exponent << 1 | mantissa >> 8;
+ buf[0] = mantissa & 0xff;
+
+ /* Calculate and return the exact baud rate. */
+ baud = (baseline / mantissa) >> (exponent << 1);
+
+ return baud;
+}
+
+static int pl2303_vendor_write(struct usbh_serial *serial, uint16_t wValue, uint16_t wIndex)
+{
+ struct usb_setup_packet *setup;
+ struct usbh_pl2303 *pl2303_class;
+
+ if (!serial || !serial->hport || !serial->priv) {
+ return -USB_ERR_INVAL;
+ }
+ setup = serial->hport->setup;
+ pl2303_class = (struct usbh_pl2303 *)serial->priv;
+
+ setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE;
+ setup->bRequest = pl2303_class->chip_type == TYPE_HXN ? PL2303_VENDOR_WRITE_NREQUEST : PL2303_VENDOR_WRITE_REQUEST;
+ setup->wValue = wValue;
+ setup->wIndex = wIndex;
+ setup->wLength = 0;
+
+ return usbh_control_transfer(serial->hport, setup, NULL);
+}
+
+static int pl2303_vendor_read(struct usbh_serial *serial, uint16_t wValue, uint8_t *data)
+{
+ struct usb_setup_packet *setup;
+ struct usbh_pl2303 *pl2303_class;
+ int ret;
+
+ if (!serial || !serial->hport || !serial->priv) {
+ return -USB_ERR_INVAL;
+ }
+ setup = serial->hport->setup;
+ pl2303_class = (struct usbh_pl2303 *)serial->priv;
+
+ setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE;
+ setup->bRequest = pl2303_class->chip_type == TYPE_HXN ? PL2303_VENDOR_READ_NREQUEST : PL2303_VENDOR_READ_REQUEST;
+ setup->wValue = wValue;
+ setup->wIndex = 0;
+ setup->wLength = 1;
+
+ ret = usbh_control_transfer(serial->hport, setup, serial->iobuffer);
+ if (ret < 0) {
+ return ret;
+ }
+ memcpy(data, serial->iobuffer, 1);
+
+ return ret;
+}
+
+static bool pl2303_supports_hx_status(struct usbh_serial *serial)
+{
+ int ret;
+ uint8_t buf;
+
+ ret = pl2303_vendor_read(serial, PL2303_READ_TYPE_HX_STATUS, &buf);
+ if (ret < 0) {
+ return false;
+ }
+
+ return true;
+}
+
+static bool pl2303_is_hxd_clone(struct usbh_serial *serial)
+{
+ struct usb_setup_packet *setup;
+ int ret;
+
+ if (!serial || !serial->hport) {
+ return -USB_ERR_INVAL;
+ }
+ setup = serial->hport->setup;
+
+ setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_DEVICE;
+ setup->bRequest = CDC_REQUEST_GET_LINE_CODING;
+ setup->wValue = 0;
+ setup->wIndex = 0;
+ setup->wLength = 7;
+
+ ret = usbh_control_transfer(serial->hport, setup, serial->iobuffer);
+ if (ret < 0) {
+ return false;
+ }
+ return true;
+}
+
+static int pl2303_update_reg(struct usbh_serial *serial, uint8_t reg, uint8_t mask, uint8_t val)
+{
+ int ret;
+ uint8_t buf[1];
+ struct usbh_pl2303 *pl2303_class;
+
+ if (!serial || !serial->hport || !serial->priv) {
+ return -USB_ERR_INVAL;
+ }
+
+ pl2303_class = (struct usbh_pl2303 *)serial->priv;
+
+ if (pl2303_class->chip_type == TYPE_HXN)
+ ret = pl2303_vendor_read(serial, reg, buf);
+ else
+ ret = pl2303_vendor_read(serial, reg | 0x80, buf);
+
+ if (ret < 0) {
+ return ret;
+ }
+
+ *buf &= ~mask;
+ *buf |= val & mask;
+
+ return pl2303_vendor_write(serial, reg, *buf);
+}
+
+static int usbh_pl2303_get_chiptype(struct usbh_serial *serial)
+{
+ if (serial->hport->device_desc.bDeviceClass == 0x02) {
+ return TYPE_H; /* variant 0 */
+ }
+
+ if (serial->hport->device_desc.bMaxPacketSize0 != 0x40) {
+ if (serial->hport->device_desc.bDeviceClass == 0x00 || serial->hport->device_desc.bDeviceClass == 0xff)
+ return TYPE_H; /* variant 1 */
+
+ return TYPE_H; /* variant 0 */
+ }
+
+ switch (serial->hport->device_desc.bcdUSB) {
+ case 0x101:
+ /* USB 1.0.1? Let's assume they meant 1.1... */
+ case 0x110:
+ switch (serial->hport->device_desc.bcdDevice) {
+ case 0x300:
+ return TYPE_HX;
+ case 0x400:
+ return TYPE_HXD;
+ default:
+ return TYPE_HX;
+ }
+ break;
+ case 0x200:
+ switch (serial->hport->device_desc.bcdDevice) {
+ case 0x100: /* GC */
+ case 0x105:
+ return TYPE_HXN;
+ case 0x300: /* GT / TA */
+ if (pl2303_supports_hx_status(serial))
+ return TYPE_TA;
+ __attribute__((fallthrough));
+ case 0x305:
+ case 0x400: /* GL */
+ case 0x405:
+ return TYPE_HXN;
+ case 0x500: /* GE / TB */
+ if (pl2303_supports_hx_status(serial))
+ return TYPE_TB;
+ __attribute__((fallthrough));
+ case 0x505:
+ case 0x600: /* GS */
+ case 0x605:
+ case 0x700: /* GR */
+ case 0x705:
+ case 0x905: /* GT-2AB */
+ case 0x1005: /* GC-Q20 */
+ return TYPE_HXN;
+ }
+ break;
+ }
+
+ USB_LOG_ERR("Unsupported PL2303 Device\r\n");
+ return -USB_ERR_NOTSUPP;
+}
+
+static int usbh_pl2303_attach(struct usbh_serial *serial)
+{
+ struct usbh_pl2303 *pl2303_class;
+ struct usb_endpoint_descriptor *ep_desc;
+ uint8_t type;
+ int ret;
+
+ ret = usbh_pl2303_get_chiptype(serial);
+ if (ret < 0) {
+ return ret;
+ }
+
+ pl2303_class = usb_osal_malloc(sizeof(struct usbh_pl2303));
+ if (pl2303_class == NULL) {
+ USB_LOG_ERR("Fail to alloc pl2303_class\r\n");
+ return -USB_ERR_NOMEM;
+ }
+ memset(pl2303_class, 0, sizeof(struct usbh_pl2303));
+ serial->priv = pl2303_class;
+
+ for (uint8_t i = 0; i < serial->hport->config.intf[serial->intf].altsetting[0].intf_desc.bNumEndpoints; i++) {
+ ep_desc = &serial->hport->config.intf[serial->intf].altsetting[0].ep[i].ep_desc;
+
+ if (USB_GET_ENDPOINT_TYPE(ep_desc->bmAttributes) == USB_ENDPOINT_TYPE_INTERRUPT) {
+ if (ep_desc->bEndpointAddress & 0x80) {
+ USBH_EP_INIT(pl2303_class->intin, ep_desc);
+ break;
+ } else {
+ }
+ }
+ }
+
+ if (!pl2303_class->intin) {
+ USB_LOG_ERR("Failed to find interrupt endpoint\r\n");
+ ret = -USB_ERR_NODEV;
+ goto errout;
+ }
+
+ type = (uint8_t)ret;
+ pl2303_class->chip_type = type;
+ pl2303_class->quirks = pl2303_type_data[pl2303_class->chip_type].quirks;
+
+ USB_LOG_INFO("chip type: %s\r\n", pl2303_type_data[pl2303_class->chip_type].name);
+
+ if (type == TYPE_HXD && pl2303_is_hxd_clone(serial)) {
+ pl2303_class->quirks |= PL2303_QUIRK_NO_BREAK_GETLINE;
+ }
+
+ if (type != TYPE_HXN) {
+ uint8_t buf[1];
+ ret = pl2303_vendor_read(serial, 0x8484, buf);
+ ret |= pl2303_vendor_write(serial, 0x0404, 0);
+ ret |= pl2303_vendor_read(serial, 0x8484, buf);
+ ret |= pl2303_vendor_read(serial, 0x8383, buf);
+ ret |= pl2303_vendor_read(serial, 0x8484, buf);
+ ret |= pl2303_vendor_write(serial, 0x0404, 1);
+ ret |= pl2303_vendor_read(serial, 0x8484, buf);
+ ret |= pl2303_vendor_read(serial, 0x8383, buf);
+ ret |= pl2303_vendor_write(serial, 0, 1);
+ ret |= pl2303_vendor_write(serial, 1, 0);
+ if (pl2303_class->quirks & PL2303_QUIRK_LEGACY)
+ ret |= pl2303_vendor_write(serial, 2, 0x24);
+ else
+ ret |= pl2303_vendor_write(serial, 2, 0x44);
+ } else {
+ ret = 0;
+ }
+
+ if (ret < 0) {
+ USB_LOG_ERR("pl2303 init failed\r\n");
+ goto errout;
+ }
+
+ return 0;
+errout:
+ serial->priv = NULL;
+ usb_osal_free(pl2303_class);
+ return ret;
+}
+
+static void usbh_pl2303_detach(struct usbh_serial *serial)
+{
+ struct usbh_pl2303 *pl2303_class;
+
+ if (!serial || !serial->priv) {
+ return;
+ }
+
+ pl2303_class = (struct usbh_pl2303 *)serial->priv;
+ if (pl2303_class->intin) {
+ usbh_kill_urb(&pl2303_class->intin_urb);
+ }
+ serial->priv = NULL;
+ usb_osal_free(pl2303_class);
+}
+
+static int usbh_pl2303_set_flow_ctrl(struct usbh_serial *serial, bool hardctrl)
+{
+ struct usbh_pl2303 *pl2303_class;
+
+ if (!serial || !serial->hport || !serial->priv) {
+ return -USB_ERR_INVAL;
+ }
+
+ pl2303_class = (struct usbh_pl2303 *)serial->priv;
+
+ if (hardctrl) {
+ if (pl2303_class->quirks & PL2303_QUIRK_LEGACY) {
+ return pl2303_update_reg(serial, 0, PL2303_FLOWCTRL_MASK, 0x40);
+ } else if (pl2303_class->chip_type == TYPE_HXN) {
+ return pl2303_update_reg(serial, PL2303_HXN_FLOWCTRL_REG,
+ PL2303_HXN_FLOWCTRL_MASK,
+ PL2303_HXN_FLOWCTRL_RTS_CTS);
+ } else {
+ return pl2303_update_reg(serial, 0, PL2303_FLOWCTRL_MASK, 0x60);
+ }
+ } else {
+ if (pl2303_class->chip_type == TYPE_HXN) {
+ return pl2303_update_reg(serial, PL2303_HXN_FLOWCTRL_REG,
+ PL2303_HXN_FLOWCTRL_MASK,
+ PL2303_HXN_FLOWCTRL_NONE);
+ } else {
+ return pl2303_update_reg(serial, 0, PL2303_FLOWCTRL_MASK, 0);
+ }
+ }
+}
+
+static int usbh_pl2303_set_line_coding(struct usbh_serial *serial, struct cdc_line_coding *line_coding)
+{
+ struct usb_setup_packet *setup;
+ struct usbh_pl2303 *pl2303_class;
+ uint32_t baud;
+ uint32_t baud_sup;
+ uint8_t buf[7];
+
+ if (!serial || !serial->hport || !serial->priv) {
+ return -USB_ERR_INVAL;
+ }
+
+ setup = serial->hport->setup;
+ pl2303_class = (struct usbh_pl2303 *)serial->priv;
+
+ setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE;
+ setup->bRequest = CDC_REQUEST_SET_LINE_CODING;
+ setup->wValue = 0;
+ setup->wIndex = serial->intf;
+ setup->wLength = 7;
+
+ baud = line_coding->dwDTERate;
+ if (pl2303_type_data[pl2303_class->chip_type].max_baud_rate) {
+ baud = MIN(baud, pl2303_type_data[pl2303_class->chip_type].max_baud_rate);
+ }
+ /*
+ * Use direct method for supported baud rates, otherwise use divisors.
+ * Newer chip types do not support divisor encoding.
+ */
+ if (pl2303_type_data[pl2303_class->chip_type].no_divisors)
+ baud_sup = baud;
+ else
+ baud_sup = pl2303_get_supported_baud_rate(baud);
+
+ if (baud == baud_sup)
+ baud = pl2303_encode_baud_rate_direct(buf, baud);
+ else if (pl2303_type_data[pl2303_class->chip_type].alt_divisors)
+ baud = pl2303_encode_baud_rate_divisor_alt(buf, baud);
+ else
+ baud = pl2303_encode_baud_rate_divisor(buf, baud);
+
+ buf[4] = line_coding->bCharFormat;
+ buf[5] = line_coding->bParityType;
+ buf[6] = line_coding->bDataBits;
+
+ memcpy(serial->iobuffer, buf, sizeof(struct cdc_line_coding));
+
+ return usbh_control_transfer(serial->hport, setup, serial->iobuffer);
+}
+
+static int usbh_pl2303_get_line_coding(struct usbh_serial *serial, struct cdc_line_coding *line_coding)
+{
+ struct usb_setup_packet *setup;
+ int ret;
+
+ if (!serial || !serial->hport) {
+ return -USB_ERR_INVAL;
+ }
+ setup = serial->hport->setup;
+
+ setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE;
+ setup->bRequest = CDC_REQUEST_GET_LINE_CODING;
+ setup->wValue = 0;
+ setup->wIndex = serial->intf;
+ setup->wLength = 7;
+
+ ret = usbh_control_transfer(serial->hport, setup, serial->iobuffer);
+ if (ret < 0) {
+ return ret;
+ }
+ memcpy(line_coding, serial->iobuffer, sizeof(struct cdc_line_coding));
+ return ret;
+}
+
+static int usbh_pl2303_set_line_state(struct usbh_serial *serial, bool dtr, bool rts)
+{
+ struct usb_setup_packet *setup;
+
+ if (!serial || !serial->hport) {
+ return -USB_ERR_INVAL;
+ }
+ setup = serial->hport->setup;
+
+ setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE;
+ setup->bRequest = CDC_REQUEST_SET_CONTROL_LINE_STATE;
+ setup->wValue = (dtr << 0) | (rts << 1);
+ setup->wIndex = serial->intf;
+ setup->wLength = 0;
+
+ return usbh_control_transfer(serial->hport, setup, NULL);
+}
+
+static int usbh_pl2303_get_modem_status(struct usbh_serial *serial)
+{
+ struct usbh_pl2303 *pl2303_class;
+ uintptr_t flags;
+ uint16_t status;
+
+ if (!serial || !serial->hport || !serial->priv) {
+ return -USB_ERR_INVAL;
+ }
+
+ flags = usb_osal_enter_critical_section();
+ pl2303_class = (struct usbh_pl2303 *)serial->priv;
+
+ status = (pl2303_class->modem_status & UART_DSR ? USBH_SERIAL_TIOCM_DSR : 0) |
+ (pl2303_class->modem_status & UART_CTS ? USBH_SERIAL_TIOCM_CTS : 0) |
+ (pl2303_class->modem_status & UART_RING ? USBH_SERIAL_TIOCM_RI : 0) |
+ (pl2303_class->modem_status & UART_DCD ? USBH_SERIAL_TIOCM_CD : 0) |
+ (serial->line_state & USBH_SERIAL_TIOCM_DTR ? USBH_SERIAL_TIOCM_DTR : 0) |
+ (serial->line_state & USBH_SERIAL_TIOCM_RTS ? USBH_SERIAL_TIOCM_RTS : 0);
+
+ usb_osal_leave_critical_section(flags);
+
+ return status;
+}
+
+#ifdef CONFIG_USBH_SERIAL_GET_MODEM_STATUS
+static int __usbh_pl2303_get_modem_status(struct usbh_serial *serial)
+{
+ struct usbh_pl2303 *pl2303_class;
+ uint8_t status = 0;
+ uint16_t difference;
+ uintptr_t flags;
+ int ret;
+
+ if (!serial || !serial->hport || !serial->priv) {
+ return -USB_ERR_INVAL;
+ }
+ pl2303_class = (struct usbh_pl2303 *)serial->priv;
+
+ usbh_int_urb_fill(&pl2303_class->intin_urb, serial->hport, pl2303_class->intin, &serial->iobuffer[USBH_SERIAL_INT_NOCACHE_OFFSET], pl2303_class->intin->wMaxPacketSize, 0xffffffff, NULL, NULL);
+ ret = usbh_submit_urb(&pl2303_class->intin_urb);
+ if (ret < 0) {
+ return ret;
+ }
+
+ if (ret < 1) {
+ return -USB_ERR_INVAL;
+ }
+
+ flags = usb_osal_enter_critical_section();
+
+ status = serial->iobuffer[USBH_SERIAL_INT_NOCACHE_OFFSET];
+ difference = pl2303_class->modem_status ^ status;
+ pl2303_class->modem_status = status;
+
+ if (status & UART_BREAK_ERROR)
+ serial->iocount.brk++;
+
+ if (difference & UART_STATE_MSR_MASK) {
+ if (difference & UART_CTS)
+ serial->iocount.cts++;
+ if (difference & UART_DSR)
+ serial->iocount.dsr++;
+ if (difference & UART_RING)
+ serial->iocount.rng++;
+ if (difference & UART_DCD) {
+ serial->iocount.dcd++;
+ }
+ }
+
+ usb_osal_leave_critical_section(flags);
+
+ return ret;
+}
+#endif
+
+static const struct usbh_serial_driver pl2303_driver = {
+ .driver_name = "pl2303",
+
+ .ignore_rx_header = 0,
+ .ignore_tx_header = 0,
+
+ .attach = usbh_pl2303_attach,
+ .detach = usbh_pl2303_detach,
+ .set_flow_control = usbh_pl2303_set_flow_ctrl,
+ .set_line_coding = usbh_pl2303_set_line_coding,
+ .get_line_coding = usbh_pl2303_get_line_coding,
+ .set_line_state = usbh_pl2303_set_line_state,
+ .get_modem_status = usbh_pl2303_get_modem_status,
+};
+
+static int usbh_pl2303_connect(struct usbh_hubport *hport, uint8_t intf)
+{
+ return usbh_serial_probe(hport, intf, &pl2303_driver) ? 0 : -USB_ERR_NOMEM;
+}
+
+static int usbh_pl2303_disconnect(struct usbh_hubport *hport, uint8_t intf)
+{
+ struct usbh_serial *serial = (struct usbh_serial *)hport->config.intf[intf].priv;
+
+ if (serial) {
+ usbh_serial_remove(serial);
+ }
+
+ return 0;
+}
+
+static const uint16_t pl2303_id_table[][2] = {
+ { 0x067B, 0x2303 }, // PL2303 Serial (ATEN/IOGEAR UC232A)
+ { 0x067B, 0x2304 }, // PL2303HXN Serial, type TB
+ { 0x067B, 0x23A3 }, // PL2303HXN Serial, type GC
+ { 0x067B, 0x23B3 }, // PL2303HXN Serial, type GB
+ { 0x067B, 0x23C3 }, // PL2303HXN Serial, type GT
+ { 0x067B, 0x23D3 }, // PL2303HXN Serial, type GL
+ { 0x067B, 0x23E3 }, // PL2303HXN Serial, type GE
+ { 0x067B, 0x23F3 }, // PL2303HXN Serial, type GS
+ { 0, 0 },
+};
+
+const struct usbh_class_driver pl2303_class_driver = {
+ .driver_name = "pl2303",
+ .connect = usbh_pl2303_connect,
+ .disconnect = usbh_pl2303_disconnect
+};
+
+CLASS_INFO_DEFINE const struct usbh_class_info pl2303_class_info = {
+ .match_flags = USB_CLASS_MATCH_VID_PID | USB_CLASS_MATCH_INTF_CLASS,
+ .bInterfaceClass = 0xff,
+ .bInterfaceSubClass = 0x00,
+ .bInterfaceProtocol = 0x00,
+ .id_table = pl2303_id_table,
+ .class_driver = &pl2303_class_driver
+}; \ No newline at end of file
diff --git a/class/serial/usbh_pl2303.h b/class/serial/usbh_pl2303.h
new file mode 100644
index 00000000..83391c16
--- /dev/null
+++ b/class/serial/usbh_pl2303.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2024 ~ 2025, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#ifndef USBH_PL2303_H
+#define USBH_PL2303_H
+
+#include "usb_cdc.h"
+
+#define PL2303_VENDOR_WRITE_REQUEST 0x01
+#define PL2303_VENDOR_WRITE_NREQUEST 0x80
+#define PL2303_VENDOR_READ_REQUEST 0x01
+#define PL2303_VENDOR_READ_NREQUEST 0x81
+
+#define PL2303_FLOWCTRL_MASK 0xf0
+
+#define PL2303_READ_TYPE_HX_STATUS 0x8080
+
+#define PL2303_HXN_RESET_REG 0x07
+#define PL2303_HXN_RESET_UPSTREAM_PIPE 0x02
+#define PL2303_HXN_RESET_DOWNSTREAM_PIPE 0x01
+
+#define PL2303_HXN_FLOWCTRL_REG 0x0a
+#define PL2303_HXN_FLOWCTRL_MASK 0x1c
+#define PL2303_HXN_FLOWCTRL_NONE 0x1c
+#define PL2303_HXN_FLOWCTRL_RTS_CTS 0x18
+#define PL2303_HXN_FLOWCTRL_XON_XOFF 0x0c
+
+#define PL2303_QUIRK_UART_STATE_IDX0 BIT(0)
+#define PL2303_QUIRK_LEGACY BIT(1)
+#define PL2303_QUIRK_ENDPOINT_HACK BIT(2)
+#define PL2303_QUIRK_NO_BREAK_GETLINE BIT(3)
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* USBH_PL2303_H */
diff --git a/class/serial/usbh_serial.c b/class/serial/usbh_serial.c
new file mode 100644
index 00000000..e4a5d105
--- /dev/null
+++ b/class/serial/usbh_serial.c
@@ -0,0 +1,719 @@
+/*
+ * Copyright (c) 2025, sakumisu
+ * Copyright (c) 2025, MDLZCOOL
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#include "usbh_core.h"
+#include "usbh_serial.h"
+
+#undef USB_DBG_TAG
+#define USB_DBG_TAG "usbh_serial"
+#include "usb_log.h"
+
+#define DEV_FORMAT_VENDOR "/dev/ttyUSB%d"
+#define DEV_FORMAT_CDC_ACM "/dev/ttyACM%d"
+
+#define CONFIG_USBHOST_MAX_SERIAL_CLASS 4
+
+static struct usbh_serial g_serial_class[CONFIG_USBHOST_MAX_SERIAL_CLASS];
+
+static uint32_t g_devinuse = 0;
+static uint32_t g_cdcacm_devinuse = 0;
+
+USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_serial_iobuffer[CONFIG_USBHOST_MAX_SERIAL_CLASS][USB_ALIGN_UP((USBH_SERIAL_INT_NOCACHE_OFFSET + USBH_SERIAL_INT_NOCACHE_SIZE), CONFIG_USB_ALIGN_SIZE)];
+
+/* refer to cherryrb */
+static int usbh_serial_ringbuffer_init(usbh_serial_ringbuf_t *rb, void *pool, uint32_t size)
+{
+ if (NULL == rb) {
+ return -1;
+ }
+
+ if (NULL == pool) {
+ return -1;
+ }
+
+ if ((size < 2) || (size & (size - 1))) {
+ return -1;
+ }
+
+ rb->in = 0;
+ rb->out = 0;
+ rb->mask = size - 1;
+ rb->pool = pool;
+
+ return 0;
+}
+
+static void usbh_serial_ringbuffer_reset(usbh_serial_ringbuf_t *rb)
+{
+ rb->in = 0;
+ rb->out = 0;
+}
+
+static uint32_t usbh_serial_ringbuffer_get_used(usbh_serial_ringbuf_t *rb)
+{
+ return rb->in - rb->out;
+}
+
+static uint32_t usbh_serial_ringbuffer_write(usbh_serial_ringbuf_t *rb, void *data, uint32_t size)
+{
+ uint32_t unused;
+ uint32_t offset;
+ uint32_t remain;
+
+ unused = (rb->mask + 1) - (rb->in - rb->out);
+
+ if (size > unused) {
+ size = unused;
+ }
+
+ offset = rb->in & rb->mask;
+
+ remain = rb->mask + 1 - offset;
+ remain = remain > size ? size : remain;
+
+ memcpy(((uint8_t *)(rb->pool)) + offset, data, remain);
+ memcpy(rb->pool, (uint8_t *)data + remain, size - remain);
+
+ rb->in += size;
+
+ return size;
+}
+
+static uint32_t usbh_serial_ringbuffer_peek(usbh_serial_ringbuf_t *rb, void *data, uint32_t size)
+{
+ uint32_t used;
+ uint32_t offset;
+ uint32_t remain;
+
+ used = rb->in - rb->out;
+ if (size > used) {
+ size = used;
+ }
+
+ offset = rb->out & rb->mask;
+
+ remain = rb->mask + 1 - offset;
+ remain = remain > size ? size : remain;
+
+ memcpy(data, ((uint8_t *)(rb->pool)) + offset, remain);
+ memcpy((uint8_t *)data + remain, rb->pool, size - remain);
+
+ return size;
+}
+
+static uint32_t usbh_serial_ringbuffer_read(usbh_serial_ringbuf_t *rb, void *data, uint32_t size)
+{
+ size = usbh_serial_ringbuffer_peek(rb, data, size);
+ rb->out += size;
+ return size;
+}
+
+static struct usbh_serial *usbh_serial_alloc(bool is_cdcacm)
+{
+ uint8_t devno;
+ uint8_t devno2;
+
+ for (devno = 0; devno < CONFIG_USBHOST_MAX_SERIAL_CLASS; devno++) {
+ if ((g_devinuse & (1U << devno)) == 0) {
+ g_devinuse |= (1U << devno);
+ memset(&g_serial_class[devno], 0, sizeof(struct usbh_serial));
+ g_serial_class[devno].minor = devno;
+ g_serial_class[devno].cdc_minor = -1;
+ g_serial_class[devno].iobuffer = g_serial_iobuffer[devno];
+ g_serial_class[devno].rx_complete_sem = usb_osal_sem_create(0);
+
+ if (is_cdcacm) {
+ for (devno2 = 0; devno2 < CONFIG_USBHOST_MAX_SERIAL_CLASS; devno2++) {
+ if ((g_cdcacm_devinuse & (1U << devno2)) == 0) {
+ g_cdcacm_devinuse |= (1U << devno2);
+ g_serial_class[devno].cdc_minor = devno2;
+ return &g_serial_class[devno];
+ }
+ }
+
+ g_devinuse &= ~(1U << devno);
+ return NULL;
+ } else {
+ return &g_serial_class[devno];
+ }
+ }
+ }
+ return NULL;
+}
+
+static void usbh_serial_free(struct usbh_serial *serial)
+{
+ uint8_t devno = serial->minor;
+ if (devno < 32) {
+ g_devinuse &= ~(1U << devno);
+ }
+
+ if (serial->cdc_minor >= 0) {
+ g_cdcacm_devinuse &= ~(1U << serial->cdc_minor);
+ }
+
+ if (g_serial_class[devno].rx_complete_sem) {
+ usb_osal_sem_delete(g_serial_class[devno].rx_complete_sem);
+ }
+}
+
+static void usbh_serial_callback(void *arg, int nbytes)
+{
+ struct usbh_serial *serial = (struct usbh_serial *)arg;
+ int ret;
+
+ if (nbytes >= serial->driver->ignore_rx_header) {
+ usbh_serial_ringbuffer_write(&serial->rx_rb,
+ &serial->iobuffer[USBH_SERIAL_RX_NOCACHE_OFFSET + serial->driver->ignore_rx_header],
+ (nbytes - serial->driver->ignore_rx_header));
+
+ /* resubmit the read urb */
+ usbh_bulk_urb_fill(&serial->bulkin_urb, serial->hport, serial->bulkin, &serial->iobuffer[USBH_SERIAL_RX_NOCACHE_OFFSET], serial->bulkin->wMaxPacketSize,
+ 0, usbh_serial_callback, serial);
+ ret = usbh_submit_urb(&serial->bulkin_urb);
+ if (ret < 0) {
+ USB_LOG_ERR("serial submit failed: %d\n", ret);
+ }
+
+ if (serial->rx_complete_callback) {
+ serial->rx_complete_callback(serial, nbytes - serial->driver->ignore_rx_header);
+ }
+ serial->rx_errorcode = 0;
+ usb_osal_sem_give(serial->rx_complete_sem);
+ } else {
+ serial->rx_errorcode = nbytes;
+ usb_osal_sem_give(serial->rx_complete_sem);
+ }
+}
+
+struct usbh_serial *usbh_serial_probe(struct usbh_hubport *hport, uint8_t intf,
+ const struct usbh_serial_driver *driver)
+{
+ struct usb_endpoint_descriptor *ep_desc;
+ struct usbh_serial *serial;
+ bool is_cdcacm = false;
+ int ret;
+
+ if (strcmp(driver->driver_name, "cdc_acm") == 0) {
+ is_cdcacm = true;
+ }
+
+ serial = usbh_serial_alloc(is_cdcacm);
+ if (serial == NULL) {
+ USB_LOG_ERR("Fail to alloc serial class\r\n");
+ return NULL;
+ }
+
+ serial->hport = hport;
+ serial->intf = intf;
+ serial->driver = driver;
+
+ if (driver->attach) {
+ ret = driver->attach(serial);
+ if (ret < 0) {
+ USB_LOG_ERR("Serial attach failed: %d\r\n", ret);
+ usbh_serial_free(serial);
+ return NULL;
+ }
+ }
+
+ if (is_cdcacm) {
+ intf = intf + 1; /* data interface */
+ }
+
+ for (uint8_t i = 0; i < hport->config.intf[intf].altsetting[0].intf_desc.bNumEndpoints; i++) {
+ ep_desc = &hport->config.intf[intf].altsetting[0].ep[i].ep_desc;
+
+ if (USB_GET_ENDPOINT_TYPE(ep_desc->bmAttributes) == USB_ENDPOINT_TYPE_BULK) {
+ if (ep_desc->bEndpointAddress & 0x80) {
+ USBH_EP_INIT(serial->bulkin, ep_desc);
+ } else {
+ USBH_EP_INIT(serial->bulkout, ep_desc);
+ }
+ }
+ }
+
+ if (is_cdcacm) {
+ intf = intf - 1; /* data interface */
+ }
+
+ if (!serial->bulkin || !serial->bulkout) {
+ USB_LOG_ERR("Serial bulk in/out endpoint not found\r\n");
+ usbh_serial_free(serial);
+ return NULL;
+ }
+
+ if (is_cdcacm) {
+ snprintf(hport->config.intf[intf].devname, CONFIG_USBHOST_DEV_NAMELEN, DEV_FORMAT_CDC_ACM, serial->cdc_minor);
+ } else {
+ snprintf(hport->config.intf[intf].devname, CONFIG_USBHOST_DEV_NAMELEN, DEV_FORMAT_VENDOR, serial->minor);
+ }
+
+ hport->config.intf[intf].priv = serial;
+ USB_LOG_INFO("Register Serial Class: %s (%s)\r\n", hport->config.intf[intf].devname, driver->driver_name);
+
+ usbh_serial_run(serial);
+
+ return serial;
+}
+
+void usbh_serial_remove(struct usbh_serial *serial)
+{
+ if (!serial || !serial->hport)
+ return;
+
+ usbh_serial_close(serial);
+
+ if (serial->driver && serial->driver->detach) {
+ serial->driver->detach(serial);
+ }
+
+ if (serial->hport->config.intf[serial->intf].priv) {
+ usb_osal_thread_schedule_other();
+ USB_LOG_INFO("Unregister Serial Class: %s (%s)\r\n", serial->hport->config.intf[serial->intf].devname, serial->driver->driver_name);
+ usbh_serial_stop(serial);
+ }
+
+ usbh_serial_free(serial);
+}
+
+struct usbh_serial *usbh_serial_open(const char *devname, uint32_t open_flags)
+{
+ struct usbh_serial *serial;
+ int ret;
+
+ serial = usbh_find_class_instance(devname);
+ if (!serial) {
+ return NULL;
+ }
+
+ if (serial->ref_count != 0) {
+ USB_LOG_ERR("Device busy: %s\r\n", devname);
+ return NULL;
+ }
+
+ if (serial && serial->driver && serial->driver->open) {
+ ret = serial->driver->open(serial);
+ if (ret < 0) {
+ return NULL;
+ }
+ }
+
+ usbh_serial_ringbuffer_init(&serial->rx_rb, serial->rx_rb_pool, CONFIG_USBHOST_SERIAL_RX_SIZE);
+
+ serial->ref_count++;
+ serial->open_flags = open_flags;
+
+ return serial;
+}
+
+int usbh_serial_close(struct usbh_serial *serial)
+{
+ if (!serial || !serial->hport) {
+ return -USB_ERR_INVAL;
+ }
+
+ if (serial->ref_count == 0) {
+ return 0;
+ }
+
+ if (serial->bulkin) {
+ usbh_kill_urb(&serial->bulkin_urb);
+ }
+ if (serial->bulkout) {
+ usbh_kill_urb(&serial->bulkout_urb);
+ }
+
+ if (serial && serial->driver && serial->driver->set_flow_control && serial->rtscts) {
+ serial->driver->set_flow_control(serial, false);
+ }
+
+ if (serial && serial->driver && serial->driver->close) {
+ serial->driver->close(serial);
+ }
+
+ serial->ref_count--;
+ serial->rtscts = false;
+
+ return 0;
+}
+
+static int usbh_serial_tiocmset(struct usbh_serial *serial, uint32_t set, uint32_t clear)
+{
+ int ret;
+ uint16_t line_state;
+ bool dtr;
+ bool rts;
+
+ if (!serial || !serial->hport || !serial->hport->connected) {
+ return -USB_ERR_INVAL;
+ }
+
+ if (serial->ref_count == 0) {
+ return -USB_ERR_NODEV;
+ }
+
+ line_state = serial->line_state;
+ clear &= ~set; /* 'set' takes precedence over 'clear' */
+
+ if (set & USBH_SERIAL_TIOCM_DTR) {
+ line_state |= USBH_SERIAL_TIOCM_DTR;
+ }
+ if (set & USBH_SERIAL_TIOCM_RTS) {
+ line_state |= USBH_SERIAL_TIOCM_RTS;
+ }
+ if (clear & USBH_SERIAL_TIOCM_DTR) {
+ line_state &= ~USBH_SERIAL_TIOCM_DTR;
+ }
+ if (clear & USBH_SERIAL_TIOCM_RTS) {
+ line_state &= ~USBH_SERIAL_TIOCM_RTS;
+ }
+
+ dtr = (line_state & USBH_SERIAL_TIOCM_RTS) ? true : false;
+ rts = (line_state & USBH_SERIAL_TIOCM_RTS) ? true : false;
+
+ if (serial && serial->driver && serial->driver->set_line_state) {
+ ret = serial->driver->set_line_state(serial, dtr, rts);
+ } else {
+ return -USB_ERR_NOTSUPP;
+ }
+ serial->line_state = line_state;
+
+ return ret;
+}
+
+int usbh_serial_control(struct usbh_serial *serial, int cmd, void *arg)
+{
+ int ret;
+
+ if (!serial || !serial->hport || !serial->hport->connected) {
+ return -USB_ERR_INVAL;
+ }
+
+ if (serial->ref_count == 0) {
+ return -USB_ERR_NODEV;
+ }
+
+ switch (cmd) {
+ case USBH_SERIAL_CMD_SET_ATTR: {
+ struct usbh_serial_termios *termios = (struct usbh_serial_termios *)arg;
+ struct cdc_line_coding line_coding;
+
+ line_coding.dwDTERate = termios->baudrate;
+ line_coding.bCharFormat = termios->stopbits;
+ line_coding.bParityType = termios->parity;
+ line_coding.bDataBits = termios->databits;
+
+ if (serial->bulkin) {
+ usbh_kill_urb(&serial->bulkin_urb);
+ }
+ if (serial->bulkout) {
+ usbh_kill_urb(&serial->bulkout_urb);
+ }
+
+ if (serial && serial->driver && serial->driver->set_line_coding) {
+ ret = serial->driver->set_line_coding(serial, &line_coding);
+ if (ret < 0) {
+ return ret;
+ }
+ } else {
+ return -USB_ERR_NOTSUPP;
+ }
+
+ memcpy(&serial->line_coding, &line_coding, sizeof(struct cdc_line_coding));
+
+ if (serial && serial->driver && serial->driver->set_flow_control) {
+ ret = serial->driver->set_flow_control(serial, termios->rtscts);
+ }
+
+ serial->rtscts = termios->rtscts;
+ serial->rx_timeout_ms = termios->rx_timeout;
+
+ ret = usbh_serial_tiocmset(serial, USBH_SERIAL_TIOCM_DTR | USBH_SERIAL_TIOCM_RTS, 0);
+ if (ret < 0) {
+ return ret;
+ }
+
+ usbh_serial_ringbuffer_reset(&serial->rx_rb);
+ usb_osal_sem_reset(serial->rx_complete_sem);
+ usbh_bulk_urb_fill(&serial->bulkin_urb, serial->hport, serial->bulkin, &serial->iobuffer[USBH_SERIAL_RX_NOCACHE_OFFSET], serial->bulkin->wMaxPacketSize,
+ 0, usbh_serial_callback, serial);
+ ret = usbh_submit_urb(&serial->bulkin_urb);
+
+ return ret;
+ } break;
+ case USBH_SERIAL_CMD_GET_ATTR: {
+ struct usbh_serial_termios *termios = (struct usbh_serial_termios *)arg;
+ struct cdc_line_coding line_coding;
+
+ if (serial && serial->driver && serial->driver->get_line_coding) {
+ return serial->driver->get_line_coding(serial, &line_coding);
+ } else {
+ memcpy(&line_coding, &serial->line_coding, sizeof(struct cdc_line_coding));
+ }
+
+ termios->baudrate = line_coding.dwDTERate;
+ termios->stopbits = line_coding.bCharFormat;
+ termios->parity = line_coding.bParityType;
+ termios->databits = line_coding.bDataBits;
+ termios->rtscts = serial->rtscts;
+ termios->rx_timeout = serial->rx_timeout_ms;
+ return 0;
+ } break;
+ case USBH_SERIAL_CMD_IOCMBIS: {
+ uint32_t flags = *(uint32_t *)arg;
+
+ return usbh_serial_tiocmset(serial, flags, 0);
+ } break;
+ case USBH_SERIAL_CMD_IOCMBIC: {
+ uint32_t flags = *(uint32_t *)arg;
+
+ return usbh_serial_tiocmset(serial, 0, flags);
+ } break;
+ case USBH_SERIAL_CMD_TIOCMSET: {
+ uint32_t flags = *(uint32_t *)arg;
+
+ uint32_t set = 0;
+ uint32_t clear = 0;
+
+ set |= (flags & USBH_SERIAL_TIOCM_DTR) ? USBH_SERIAL_TIOCM_DTR : 0;
+ set |= (flags & USBH_SERIAL_TIOCM_RTS) ? USBH_SERIAL_TIOCM_RTS : 0;
+ clear |= !(flags & USBH_SERIAL_TIOCM_DTR) ? USBH_SERIAL_TIOCM_DTR : 0;
+ clear |= !(flags & USBH_SERIAL_TIOCM_RTS) ? USBH_SERIAL_TIOCM_RTS : 0;
+
+ return usbh_serial_tiocmset(serial, set, clear);
+ } break;
+ case USBH_SERIAL_CMD_TIOCMGET: {
+ uint32_t *flags = (uint32_t *)arg;
+ int status;
+
+ if (serial && serial->driver && serial->driver->get_modem_status) {
+ status = serial->driver->get_modem_status(serial);
+ if (status < 0) {
+ return status;
+ }
+ } else {
+ return -USB_ERR_NOTSUPP;
+ }
+ *flags = status;
+ } break;
+ default:
+ break;
+ }
+
+ return -USB_ERR_NOTSUPP;
+}
+
+int usbh_serial_write(struct usbh_serial *serial, const void *buffer, uint32_t buflen)
+{
+ int ret;
+ struct usbh_urb *urb;
+
+ if (!serial || !serial->hport || !serial->hport->connected || !serial->bulkout) {
+ return -USB_ERR_INVAL;
+ }
+
+ if (serial->ref_count == 0) {
+ return -USB_ERR_NODEV;
+ }
+
+ urb = &serial->bulkout_urb;
+
+ usbh_bulk_urb_fill(urb, serial->hport, serial->bulkout, (uint8_t *)buffer, serial->line_coding.dwDTERate ? MIN(buflen, serial->bulkout->wMaxPacketSize) : buflen, 0xffffffff, NULL, NULL);
+ ret = usbh_submit_urb(urb);
+ if (ret == 0) {
+ ret = urb->actual_length;
+ }
+ return ret;
+}
+
+int usbh_serial_read(struct usbh_serial *serial, void *buffer, uint32_t buflen)
+{
+ int ret;
+
+ if (!serial || !serial->hport || !serial->hport->connected || !serial->bulkin || !serial->line_coding.dwDTERate) {
+ return -USB_ERR_INVAL;
+ }
+
+ if (serial->ref_count == 0) {
+ return -USB_ERR_NODEV;
+ }
+
+ if (serial->open_flags & USBH_SERIAL_O_NONBLOCK) {
+ return usbh_serial_ringbuffer_read(&serial->rx_rb, buffer, buflen);
+ } else {
+ if (usbh_serial_ringbuffer_get_used(&serial->rx_rb) == 0) {
+ ret = usb_osal_sem_take(serial->rx_complete_sem, serial->rx_timeout_ms == 0 ? USB_OSAL_WAITING_FOREVER : serial->rx_timeout_ms);
+ if (ret < 0) {
+ return ret;
+ }
+ if (serial->rx_errorcode < 0) {
+ return serial->rx_errorcode;
+ }
+ }
+ return usbh_serial_ringbuffer_read(&serial->rx_rb, buffer, buflen);
+ }
+}
+
+int usbh_serial_cdc_write_async(struct usbh_serial *serial, uint8_t *buffer, uint32_t buflen, usbh_complete_callback_t complete, void *arg)
+{
+ struct usbh_urb *urb;
+
+ if (!serial || !serial->hport || !serial->hport->connected || !serial->bulkout || !complete || serial->line_coding.dwDTERate) {
+ return -USB_ERR_INVAL;
+ }
+
+ if (serial->ref_count > 0) {
+ return -USB_ERR_NODEV;
+ }
+
+ urb = &serial->bulkout_urb;
+
+ usbh_bulk_urb_fill(urb, serial->hport, serial->bulkout, buffer, buflen,
+ 0, complete, serial);
+ return usbh_submit_urb(urb);
+}
+
+int usbh_serial_cdc_read_async(struct usbh_serial *serial, uint8_t *buffer, uint32_t buflen, usbh_complete_callback_t complete, void *arg)
+{
+ struct usbh_urb *urb;
+
+ if (!serial || !serial->hport || !serial->hport->connected || !serial->bulkin || !complete || serial->line_coding.dwDTERate) {
+ return -USB_ERR_INVAL;
+ }
+
+ if (serial->ref_count > 0) {
+ return -USB_ERR_NODEV;
+ }
+
+ if (buflen % serial->bulkin->wMaxPacketSize) {
+ return -USB_ERR_INVAL;
+ }
+
+ urb = &serial->bulkin_urb;
+
+ usbh_bulk_urb_fill(urb, serial->hport, serial->bulkin, buffer, MIN(buflen, serial->bulkin->wMaxPacketSize),
+ 0, complete, serial);
+ return usbh_submit_urb(urb);
+}
+
+void usbh_serial_help(void)
+{
+ USB_LOG_RAW("USB host serial test\r\n"
+ "Usage: usbh_serial <ttypath> [options]...\r\n"
+ "\r\n"
+ "-b <baud> set serial baudrate\r\n"
+ "-t <dtr> <rts> set rts and dtr\r\n"
+ "-w string write string\r\n"
+ "-r read data and dump\r\n"
+ "-x close serial device\r\n"
+ "\r\n");
+}
+
+static USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_serial_testbuffer[512];
+
+int usbh_serial(int argc, char **argv)
+{
+ static struct usbh_serial *serial;
+ int ret;
+
+ if (argc < 3) {
+ usbh_serial_help();
+ return 0;
+ }
+
+ if (!serial) {
+ serial = usbh_serial_open(argv[1], USBH_SERIAL_O_RDWR | USBH_SERIAL_O_NONBLOCK);
+ if (!serial) {
+ USB_LOG_ERR("Fail to open serial device: %s\r\n", argv[1]);
+ return -USB_ERR_INVAL;
+ }
+ }
+
+ if (strncmp(argv[2], "-b", 2) == 0 && argc >= 4) {
+ struct usbh_serial_termios termios;
+
+ memset(&termios, 0, sizeof(termios));
+ termios.baudrate = atoi(argv[3]);
+ termios.stopbits = 0;
+ termios.parity = 0;
+ termios.databits = 8;
+ termios.rtscts = false;
+ termios.rx_timeout = 0;
+ usbh_serial_control(serial, USBH_SERIAL_CMD_SET_ATTR, &termios);
+ } else if (strncmp(argv[2], "-t", 2) == 0 && argc >= 5) {
+ uint32_t flags;
+
+ flags = atoi(argv[3]) ? USBH_SERIAL_TIOCM_DTR : 0;
+ flags |= atoi(argv[4]) ? USBH_SERIAL_TIOCM_RTS : 0;
+
+ usbh_serial_control(serial, USBH_SERIAL_CMD_TIOCMSET, &flags);
+ USB_LOG_INFO("Set DTR: %d, RTS: %d\r\n", atoi(argv[3]), atoi(argv[4]));
+ } else if (strncmp(argv[2], "-w", 2) == 0 && argc >= 4) {
+ memcpy(g_serial_testbuffer, argv[3], MIN(strlen(argv[3]), sizeof(g_serial_testbuffer)));
+ uint32_t len = snprintf((char *)g_serial_testbuffer, sizeof(g_serial_testbuffer), "%s\r\n", argv[3]);
+ ret = usbh_serial_write(serial, g_serial_testbuffer, len);
+ if (ret >= 0) {
+ USB_LOG_INFO("Write %d bytes\r\n", ret);
+ } else {
+ USB_LOG_ERR("Write failed: %d\r\n", ret);
+ }
+ } else if (strncmp(argv[2], "-r", 2) == 0) {
+ ret = usbh_serial_read(serial, g_serial_testbuffer, sizeof(g_serial_testbuffer));
+ if (ret >= 0) {
+ usb_hexdump(g_serial_testbuffer, ret);
+ USB_LOG_INFO("Read %d bytes\r\n", ret);
+ } else {
+ USB_LOG_ERR("Read failed: %d\r\n", ret);
+ }
+ } else if (strncmp(argv[2], "-x", 2) == 0) {
+ usbh_serial_close(serial);
+ serial = NULL;
+ } else {
+ usbh_serial_help();
+ }
+
+ return 0;
+}
+
+__WEAK void usbh_serial_run(struct usbh_serial *serial)
+{
+ (void)serial;
+}
+
+__WEAK void usbh_serial_stop(struct usbh_serial *serial)
+{
+ (void)serial;
+}
+
+static int usbh_cdc_data_connect(struct usbh_hubport *hport, uint8_t intf)
+{
+ (void)hport;
+ (void)intf;
+ return 0;
+}
+
+static int usbh_cdc_data_disconnect(struct usbh_hubport *hport, uint8_t intf)
+{
+ (void)hport;
+ (void)intf;
+ return 0;
+}
+
+const struct usbh_class_driver cdc_data_class_driver = {
+ .driver_name = "cdc_data",
+ .connect = usbh_cdc_data_connect,
+ .disconnect = usbh_cdc_data_disconnect
+};
+
+CLASS_INFO_DEFINE const struct usbh_class_info cdc_data_class_info = {
+ .match_flags = USB_CLASS_MATCH_INTF_CLASS,
+ .bInterfaceClass = USB_DEVICE_CLASS_CDC_DATA,
+ .bInterfaceSubClass = 0x00,
+ .bInterfaceProtocol = 0x00,
+ .id_table = NULL,
+ .class_driver = &cdc_data_class_driver
+}; \ No newline at end of file
diff --git a/class/serial/usbh_serial.h b/class/serial/usbh_serial.h
new file mode 100644
index 00000000..70c5d9d2
--- /dev/null
+++ b/class/serial/usbh_serial.h
@@ -0,0 +1,179 @@
+/*
+ * Copyright (c) 2025, sakumisu
+ * Copyright (c) 2025, MDLZCOOL
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#ifndef USBH_SERIAL_H
+#define USBH_SERIAL_H
+
+#include "usb_cdc.h"
+
+#define USBH_SERIAL_CTRL_NOCACHE_SIZE 32
+#define USBH_SERIAL_CTRL_NOCACHE_OFFSET 0
+#define USBH_SERIAL_INT_NOCACHE_SIZE 32
+#define USBH_SERIAL_INT_NOCACHE_OFFSET USB_ALIGN_UP(USBH_SERIAL_CTRL_NOCACHE_SIZE, CONFIG_USB_ALIGN_SIZE)
+#define USBH_SERIAL_RX_NOCACHE_OFFSET USB_ALIGN_UP((USBH_SERIAL_INT_NOCACHE_OFFSET + USBH_SERIAL_INT_NOCACHE_SIZE), CONFIG_USB_ALIGN_SIZE)
+#define USBH_SERIAL_RX_NOCACHE_SIZE 512
+
+#define USBH_SERIAL_DATABITS_5 5
+#define USBH_SERIAL_DATABITS_6 6
+#define USBH_SERIAL_DATABITS_7 7
+#define USBH_SERIAL_DATABITS_8 8
+
+#define USBH_SERIAL_PARITY_NONE 0
+#define USBH_SERIAL_PARITY_ODD 1
+#define USBH_SERIAL_PARITY_EVEN 2
+#define USBH_SERIAL_PARITY_MARK 3
+#define USBH_SERIAL_PARITY_SPACE 4
+
+#define USBH_SERIAL_STOPBITS_1 0
+#define USBH_SERIAL_STOPBITS_1_5 1
+#define USBH_SERIAL_STOPBITS_2 2
+
+/* modem lines */
+#define USBH_SERIAL_TIOCM_LE 0x001 /* line enable */
+#define USBH_SERIAL_TIOCM_DTR 0x002 /* data terminal ready */
+#define USBH_SERIAL_TIOCM_RTS 0x004 /* request to send */
+#define USBH_SERIAL_TIOCM_ST 0x010 /* secondary transmit */
+#define USBH_SERIAL_TIOCM_SR 0x020 /* secondary receive */
+#define USBH_SERIAL_TIOCM_CTS 0x040 /* clear to send */
+#define USBH_SERIAL_TIOCM_CAR 0x100 /* carrier detect */
+#define USBH_SERIAL_TIOCM_CD USBH_SERIAL_TIOCM_CAR
+#define USBH_SERIAL_TIOCM_RNG 0x200 /* ring */
+#define USBH_SERIAL_TIOCM_RI USBH_SERIAL_TIOCM_RNG
+#define USBH_SERIAL_TIOCM_DSR 0x400 /* data set ready */
+#define USBH_SERIAL_TIOCM_OUT1 0x2000
+#define USBH_SERIAL_TIOCM_OUT2 0x4000
+#define USBH_SERIAL_TIOCM_LOOP 0x8000
+
+#define USBH_SERIAL_O_RDONLY 0x0000 /* open for reading only */
+#define USBH_SERIAL_O_WRONLY 0x0001 /* open for writing only */
+#define USBH_SERIAL_O_RDWR 0x0002 /* open for reading and writing */
+
+#define USBH_SERIAL_O_ACCMODE 0x0003 /* mask for above modes, from 4.4BSD https://minnie.tuhs.org/cgi-bin/utree.pl?file=4.4BSD/usr/include/sys/fcntl.h */
+#define USBH_SERIAL_O_NONBLOCK 0x0004 /* non blocking I/O, from BSD apple https://opensource.apple.com/source/xnu/xnu-1228.0.2/bsd/sys/fcntl.h */
+
+#define USBH_SERIAL_CMD_SET_ATTR 0
+#define USBH_SERIAL_CMD_GET_ATTR 1
+#define USBH_SERIAL_CMD_IOCMBIS 2
+#define USBH_SERIAL_CMD_IOCMBIC 3
+#define USBH_SERIAL_CMD_TIOCMSET 4
+#define USBH_SERIAL_CMD_TIOCMGET 5
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct {
+ uint32_t in; /*!< Define the write pointer. */
+ uint32_t out; /*!< Define the read pointer. */
+ uint32_t mask; /*!< Define the write and read pointer mask. */
+ void *pool; /*!< Define the memory pointer. */
+} usbh_serial_ringbuf_t;
+
+/*
+ * Counters of the input lines (CTS, DSR, RI, CD) interrupts
+ */
+
+struct usbh_serial_async_icount {
+ uint32_t cts, dsr, rng, dcd, tx, rx;
+ uint32_t frame, parity, overrun, brk;
+ uint32_t buf_overrun;
+};
+
+struct usbh_serial_termios {
+ uint32_t baudrate;
+ uint8_t databits;
+ uint8_t parity;
+ uint8_t stopbits;
+ bool rtscts; /* hardware flow control */
+ uint32_t rx_timeout;
+};
+
+struct usbh_serial;
+
+typedef void (*usbh_serial_rx_complete_callback_t)(struct usbh_serial *serial, int nbytes);
+
+/**
+ * @brief Serial Driver Operations
+ */
+struct usbh_serial_driver {
+ const char *driver_name;
+
+ uint8_t ignore_tx_header;
+ uint8_t ignore_rx_header;
+
+ int (*attach)(struct usbh_serial *serial);
+ void (*detach)(struct usbh_serial *serial);
+
+ int (*open)(struct usbh_serial *serial);
+ void (*close)(struct usbh_serial *serial);
+ int (*set_flow_control)(struct usbh_serial *serial, bool enable);
+ int (*set_line_coding)(struct usbh_serial *serial, struct cdc_line_coding *line_coding);
+ int (*get_line_coding)(struct usbh_serial *serial, struct cdc_line_coding *line_coding);
+ int (*set_line_state)(struct usbh_serial *serial, bool dtr, bool rts);
+ int (*get_modem_status)(struct usbh_serial *serial);
+};
+
+/**
+ * @brief Serial Instance
+ */
+struct usbh_serial {
+ struct usbh_hubport *hport;
+ uint8_t intf; /* Interface Number */
+ int minor; /* Serial Port Number (/dev/ttyUSBx or /dev/ttyACMx) */
+ int cdc_minor; /* Serial Port Number (/dev/ttyACMx) */
+ uint8_t *iobuffer; /* I/O buffer for serial transfers */
+ uint8_t ref_count; /* Reference Count */
+ uint32_t open_flags;
+ uint32_t rx_timeout_ms;
+
+ struct cdc_line_coding line_coding;
+ uint16_t line_state;
+ bool rtscts; /* hardware flow control */
+ struct usbh_serial_async_icount iocount;
+
+ struct usb_endpoint_descriptor *bulkin; /* Bulk IN endpoint */
+ struct usb_endpoint_descriptor *bulkout; /* Bulk OUT endpoint */
+ struct usbh_urb bulkout_urb;
+ struct usbh_urb bulkin_urb;
+
+ const struct usbh_serial_driver *driver;
+
+ usbh_serial_ringbuf_t rx_rb;
+ uint8_t rx_rb_pool[CONFIG_USBHOST_SERIAL_RX_SIZE];
+ usb_osal_sem_t rx_complete_sem;
+ int rx_errorcode;
+ usbh_serial_rx_complete_callback_t rx_complete_callback;
+
+ void *priv; /* Private Data */
+ void *user_data; /* User Data */
+};
+
+/* internal api */
+struct usbh_serial *usbh_serial_probe(struct usbh_hubport *hport, uint8_t intf, const struct usbh_serial_driver *driver);
+void usbh_serial_remove(struct usbh_serial *serial);
+
+/* public api */
+struct usbh_serial *usbh_serial_open(const char *devname, uint32_t open_flags);
+int usbh_serial_close(struct usbh_serial *serial);
+int usbh_serial_control(struct usbh_serial *serial, int cmd, void *arg);
+int usbh_serial_write(struct usbh_serial *serial, const void *buffer, uint32_t buflen);
+int usbh_serial_read(struct usbh_serial *serial, void *buffer, uint32_t buflen);
+
+/* cdc only api */
+int usbh_serial_cdc_write_async(struct usbh_serial *serial, uint8_t *buffer, uint32_t buflen, usbh_complete_callback_t complete, void *arg);
+int usbh_serial_cdc_read_async(struct usbh_serial *serial, uint8_t *buffer, uint32_t buflen, usbh_complete_callback_t complete, void *arg);
+
+/* public weak api */
+void usbh_serial_run(struct usbh_serial *serial);
+void usbh_serial_stop(struct usbh_serial *serial);
+
+int usbh_serial(int argc, char **argv);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* USBH_SERIAL_H */ \ No newline at end of file
diff --git a/class/vendor/serial/usbh_ch34x.c b/class/vendor/serial/usbh_ch34x.c
deleted file mode 100644
index 3941d47d..00000000
--- a/class/vendor/serial/usbh_ch34x.c
+++ /dev/null
@@ -1,379 +0,0 @@
-/*
- * Copyright (c) 2024, sakumisu
- *
- * SPDX-License-Identifier: Apache-2.0
- */
-#include "usbh_core.h"
-#include "usbh_ch34x.h"
-
-#define DEV_FORMAT "/dev/ttyUSB%d"
-
-USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_ch34x_buf[USB_ALIGN_UP(64, CONFIG_USB_ALIGN_SIZE)];
-
-#define CONFIG_USBHOST_MAX_CP210X_CLASS 1
-
-static struct usbh_ch34x g_ch34x_class[CONFIG_USBHOST_MAX_CP210X_CLASS];
-static uint32_t g_devinuse = 0;
-
-static struct usbh_ch34x *usbh_ch34x_class_alloc(void)
-{
- uint8_t devno;
-
- for (devno = 0; devno < CONFIG_USBHOST_MAX_CP210X_CLASS; devno++) {
- if ((g_devinuse & (1U << devno)) == 0) {
- g_devinuse |= (1U << devno);
- memset(&g_ch34x_class[devno], 0, sizeof(struct usbh_ch34x));
- g_ch34x_class[devno].minor = devno;
- return &g_ch34x_class[devno];
- }
- }
- return NULL;
-}
-
-static void usbh_ch34x_class_free(struct usbh_ch34x *ch34x_class)
-{
- uint8_t devno = ch34x_class->minor;
-
- if (devno < 32) {
- g_devinuse &= ~(1U << devno);
- }
- memset(ch34x_class, 0, sizeof(struct usbh_ch34x));
-}
-
-static int usbh_ch34x_get_baudrate_div(uint32_t baudrate, uint8_t *factor, uint8_t *divisor)
-{
- uint8_t a;
- uint8_t b;
- uint32_t c;
-
- switch (baudrate) {
- case 921600:
- a = 0xf3;
- b = 7;
- break;
-
- case 307200:
- a = 0xd9;
- b = 7;
- break;
-
- default:
- if (baudrate > 6000000 / 255) {
- b = 3;
- c = 6000000;
- } else if (baudrate > 750000 / 255) {
- b = 2;
- c = 750000;
- } else if (baudrate > 93750 / 255) {
- b = 1;
- c = 93750;
- } else {
- b = 0;
- c = 11719;
- }
- a = (uint8_t)(c / baudrate);
- if (a == 0 || a == 0xFF) {
- return -USB_ERR_INVAL;
- }
- if ((c / a - baudrate) > (baudrate - c / (a + 1))) {
- a++;
- }
- a = (uint8_t)(256 - a);
- break;
- }
-
- *factor = a;
- *divisor = b;
-
- return 0;
-}
-
-static int usbh_ch34x_get_version(struct usbh_ch34x *ch34x_class)
-{
- struct usb_setup_packet *setup;
- int ret;
-
- if (!ch34x_class || !ch34x_class->hport) {
- return -USB_ERR_INVAL;
- }
- setup = ch34x_class->hport->setup;
-
- setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE;
- setup->bRequest = CH34X_READ_VERSION;
- setup->wValue = 0;
- setup->wIndex = 0;
- setup->wLength = 2;
-
- ret = usbh_control_transfer(ch34x_class->hport, setup, g_ch34x_buf);
- if (ret < 0) {
- return ret;
- }
-
- USB_LOG_INFO("Ch34x chip version %02x:%02x\r\n", g_ch34x_buf[0], g_ch34x_buf[1]);
- return ret;
-}
-
-static int usbh_ch34x_flow_ctrl(struct usbh_ch34x *ch34x_class)
-{
- struct usb_setup_packet *setup;
-
- if (!ch34x_class || !ch34x_class->hport) {
- return -USB_ERR_INVAL;
- }
- setup = ch34x_class->hport->setup;
-
- setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE;
- setup->bRequest = CH34X_WRITE_REG;
- setup->wValue = 0x2727;
- setup->wIndex = 0;
- setup->wLength = 0;
-
- return usbh_control_transfer(ch34x_class->hport, setup, NULL);
-}
-
-int usbh_ch34x_set_line_coding(struct usbh_ch34x *ch34x_class, struct cdc_line_coding *line_coding)
-{
- struct usb_setup_packet *setup;
- uint16_t reg_value = 0;
- uint16_t value = 0;
- uint8_t factor = 0;
- uint8_t divisor = 0;
-
- if (!ch34x_class || !ch34x_class->hport) {
- return -USB_ERR_INVAL;
- }
- setup = ch34x_class->hport->setup;
-
- memcpy((uint8_t *)&ch34x_class->line_coding, line_coding, sizeof(struct cdc_line_coding));
-
- /* refer to https://github.com/WCHSoftGroup/ch341ser_linux/blob/main/driver/ch341.c */
-
- switch (line_coding->bParityType) {
- case 0:
- break;
- case 1:
- reg_value |= CH341_L_PO;
- break;
- case 2:
- reg_value |= CH341_L_PE;
- break;
- case 3:
- reg_value |= CH341_L_PM;
- break;
- case 4:
- reg_value |= CH341_L_PS;
- break;
- default:
- return -USB_ERR_INVAL;
- }
-
- switch (line_coding->bDataBits) {
- case 5:
- reg_value |= CH341_L_D5;
- break;
- case 6:
- reg_value |= CH341_L_D6;
- break;
- case 7:
- reg_value |= CH341_L_D7;
- break;
- case 8:
- reg_value |= CH341_L_D8;
- break;
- default:
- return -USB_ERR_INVAL;
- }
-
- if (line_coding->bCharFormat == 2) {
- reg_value |= CH341_L_SB;
- }
-
- reg_value |= 0xC0;
-
- value |= 0x9c;
- value |= reg_value << 8;
-
- usbh_ch34x_get_baudrate_div(line_coding->dwDTERate, &factor, &divisor);
-
- setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE;
- setup->bRequest = CH34X_SERIAL_INIT;
- setup->wValue = value;
- setup->wIndex = (factor << 8) | 0x80 | divisor;
- setup->wLength = 0;
-
- return usbh_control_transfer(ch34x_class->hport, setup, NULL);
-}
-
-int usbh_ch34x_get_line_coding(struct usbh_ch34x *ch34x_class, struct cdc_line_coding *line_coding)
-{
- memcpy(line_coding, (uint8_t *)&ch34x_class->line_coding, sizeof(struct cdc_line_coding));
- return 0;
-}
-
-int usbh_ch34x_set_line_state(struct usbh_ch34x *ch34x_class, bool dtr, bool rts)
-{
- struct usb_setup_packet *setup;
-
- if (!ch34x_class || !ch34x_class->hport) {
- return -USB_ERR_INVAL;
- }
- setup = ch34x_class->hport->setup;
-
- setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE;
- setup->bRequest = CH34X_MODEM_CTRL;
- setup->wValue = 0x0f | (dtr << 5) | (rts << 6);
- setup->wIndex = 0;
- setup->wLength = 0;
-
- return usbh_control_transfer(ch34x_class->hport, setup, NULL);
-}
-
-static int usbh_ch34x_connect(struct usbh_hubport *hport, uint8_t intf)
-{
- struct usb_endpoint_descriptor *ep_desc;
- int ret = 0;
-
- struct usbh_ch34x *ch34x_class = usbh_ch34x_class_alloc();
- if (ch34x_class == NULL) {
- USB_LOG_ERR("Fail to alloc ch34x_class\r\n");
- return -USB_ERR_NOMEM;
- }
-
- ch34x_class->hport = hport;
- ch34x_class->intf = intf;
-
- hport->config.intf[intf].priv = ch34x_class;
-
- usbh_ch34x_get_version(ch34x_class);
- usbh_ch34x_flow_ctrl(ch34x_class);
-
- for (uint8_t i = 0; i < hport->config.intf[intf].altsetting[0].intf_desc.bNumEndpoints; i++) {
- ep_desc = &hport->config.intf[intf].altsetting[0].ep[i].ep_desc;
- if (USB_GET_ENDPOINT_TYPE(ep_desc->bmAttributes) == USB_ENDPOINT_TYPE_INTERRUPT) {
- continue;
- } else {
- if (ep_desc->bEndpointAddress & 0x80) {
- USBH_EP_INIT(ch34x_class->bulkin, ep_desc);
- } else {
- USBH_EP_INIT(ch34x_class->bulkout, ep_desc);
- }
- }
- }
-
- snprintf(hport->config.intf[intf].devname, CONFIG_USBHOST_DEV_NAMELEN, DEV_FORMAT, ch34x_class->minor);
-
- USB_LOG_INFO("Register CH34X Class:%s\r\n", hport->config.intf[intf].devname);
-
-#if 0
- USB_LOG_INFO("Test ch34x rx and tx and rx for 5 times, baudrate is 115200\r\n");
-
- struct cdc_line_coding linecoding;
- uint8_t count = 5;
-
- linecoding.dwDTERate = 115200;
- linecoding.bDataBits = 8;
- linecoding.bParityType = 0;
- linecoding.bCharFormat = 0;
- usbh_ch34x_set_line_coding(ch34x_class, &linecoding);
- usbh_ch34x_set_line_state(ch34x_class, true, false);
-
- memset(g_ch34x_buf, 'a', sizeof(g_ch34x_buf));
- ret = usbh_ch34x_bulk_out_transfer(ch34x_class, g_ch34x_buf, sizeof(g_ch34x_buf), 0xfffffff);
- USB_LOG_RAW("out ret:%d\r\n", ret);
- while (count--) {
- ret = usbh_ch34x_bulk_in_transfer(ch34x_class, g_ch34x_buf, sizeof(g_ch34x_buf), 0xfffffff);
- USB_LOG_RAW("in ret:%d\r\n", ret);
- if (ret > 0) {
- for (uint32_t i = 0; i < ret; i++) {
- USB_LOG_RAW("%02x ", g_ch34x_buf[i]);
- }
- USB_LOG_RAW("\r\n");
- }
- }
-#endif
- usbh_ch34x_run(ch34x_class);
- return ret;
-}
-
-static int usbh_ch34x_disconnect(struct usbh_hubport *hport, uint8_t intf)
-{
- int ret = 0;
-
- struct usbh_ch34x *ch34x_class = (struct usbh_ch34x *)hport->config.intf[intf].priv;
-
- if (ch34x_class) {
- if (ch34x_class->bulkin) {
- usbh_kill_urb(&ch34x_class->bulkin_urb);
- }
-
- if (ch34x_class->bulkout) {
- usbh_kill_urb(&ch34x_class->bulkout_urb);
- }
-
- if (hport->config.intf[intf].devname[0] != '\0') {
- usb_osal_thread_schedule_other();
- USB_LOG_INFO("Unregister CH34X Class:%s\r\n", hport->config.intf[intf].devname);
- usbh_ch34x_stop(ch34x_class);
- }
-
- usbh_ch34x_class_free(ch34x_class);
- }
-
- return ret;
-}
-
-int usbh_ch34x_bulk_in_transfer(struct usbh_ch34x *ch34x_class, uint8_t *buffer, uint32_t buflen, uint32_t timeout)
-{
- int ret;
- struct usbh_urb *urb = &ch34x_class->bulkin_urb;
-
- usbh_bulk_urb_fill(urb, ch34x_class->hport, ch34x_class->bulkin, buffer, buflen, timeout, NULL, NULL);
- ret = usbh_submit_urb(urb);
- if (ret == 0) {
- ret = urb->actual_length;
- }
- return ret;
-}
-
-int usbh_ch34x_bulk_out_transfer(struct usbh_ch34x *ch34x_class, uint8_t *buffer, uint32_t buflen, uint32_t timeout)
-{
- int ret;
- struct usbh_urb *urb = &ch34x_class->bulkout_urb;
-
- usbh_bulk_urb_fill(urb, ch34x_class->hport, ch34x_class->bulkout, buffer, buflen, timeout, NULL, NULL);
- ret = usbh_submit_urb(urb);
- if (ret == 0) {
- ret = urb->actual_length;
- }
- return ret;
-}
-
-__WEAK void usbh_ch34x_run(struct usbh_ch34x *ch34x_class)
-{
- (void)ch34x_class;
-}
-
-__WEAK void usbh_ch34x_stop(struct usbh_ch34x *ch34x_class)
-{
- (void)ch34x_class;
-}
-
-static const uint16_t ch34x_id_table[][2] = {
- { 0x1A86, 0x7523 },
- { 0, 0 },
-};
-
-const struct usbh_class_driver ch34x_class_driver = {
- .driver_name = "ch34x",
- .connect = usbh_ch34x_connect,
- .disconnect = usbh_ch34x_disconnect
-};
-
-CLASS_INFO_DEFINE const struct usbh_class_info ch34x_class_info = {
- .match_flags = USB_CLASS_MATCH_VID_PID | USB_CLASS_MATCH_INTF_CLASS,
- .bInterfaceClass = 0xff,
- .bInterfaceSubClass = 0x00,
- .bInterfaceProtocol = 0x00,
- .id_table = ch34x_id_table,
- .class_driver = &ch34x_class_driver
-}; \ No newline at end of file
diff --git a/class/vendor/serial/usbh_ch34x.h b/class/vendor/serial/usbh_ch34x.h
deleted file mode 100644
index c90bf9bd..00000000
--- a/class/vendor/serial/usbh_ch34x.h
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright (c) 2024, sakumisu
- *
- * SPDX-License-Identifier: Apache-2.0
- */
-#ifndef USBH_CH34X_H
-#define USBH_CH34X_H
-
-#include "usb_cdc.h"
-
-/* Requests */
-#define CH34X_READ_VERSION 0x5F
-#define CH34X_WRITE_REG 0x9A
-#define CH34X_READ_REG 0x95
-#define CH34X_SERIAL_INIT 0xA1
-#define CH34X_MODEM_CTRL 0xA4
-
-// modem control bits
-#define CH34X_BIT_RTS (1 << 6)
-#define CH34X_BIT_DTR (1 << 5)
-
-#define CH341_CTO_O 0x10
-#define CH341_CTO_D 0x20
-#define CH341_CTO_R 0x40
-#define CH341_CTI_C 0x01
-#define CH341_CTI_DS 0x02
-#define CH341_CTRL_RI 0x04
-#define CH341_CTI_DC 0x08
-#define CH341_CTI_ST 0x0f
-
-#define CH341_L_ER 0x80
-#define CH341_L_ET 0x40
-#define CH341_L_PS 0x38
-#define CH341_L_PM 0x28
-#define CH341_L_PE 0x18
-#define CH341_L_PO 0x08
-#define CH341_L_SB 0x04
-#define CH341_L_D8 0x03
-#define CH341_L_D7 0x02
-#define CH341_L_D6 0x01
-#define CH341_L_D5 0x00
-
-struct usbh_ch34x {
- struct usbh_hubport *hport;
- struct usb_endpoint_descriptor *bulkin; /* Bulk IN endpoint */
- struct usb_endpoint_descriptor *bulkout; /* Bulk OUT endpoint */
- struct usbh_urb bulkout_urb;
- struct usbh_urb bulkin_urb;
-
- struct cdc_line_coding line_coding;
-
- uint8_t intf;
- uint8_t minor;
-
- void *user_data;
-};
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-int usbh_ch34x_set_line_coding(struct usbh_ch34x *ch34x_class, struct cdc_line_coding *line_coding);
-int usbh_ch34x_get_line_coding(struct usbh_ch34x *ch34x_class, struct cdc_line_coding *line_coding);
-int usbh_ch34x_set_line_state(struct usbh_ch34x *ch34x_class, bool dtr, bool rts);
-
-int usbh_ch34x_bulk_in_transfer(struct usbh_ch34x *ch34x_class, uint8_t *buffer, uint32_t buflen, uint32_t timeout);
-int usbh_ch34x_bulk_out_transfer(struct usbh_ch34x *ch34x_class, uint8_t *buffer, uint32_t buflen, uint32_t timeout);
-
-void usbh_ch34x_run(struct usbh_ch34x *ch34x_class);
-void usbh_ch34x_stop(struct usbh_ch34x *ch34x_class);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* USBH_CH34X_H */
diff --git a/class/vendor/serial/usbh_cp210x.c b/class/vendor/serial/usbh_cp210x.c
deleted file mode 100644
index f58350e6..00000000
--- a/class/vendor/serial/usbh_cp210x.c
+++ /dev/null
@@ -1,328 +0,0 @@
-/*
- * Copyright (c) 2024, sakumisu
- *
- * SPDX-License-Identifier: Apache-2.0
- */
-#include "usbh_core.h"
-#include "usbh_cp210x.h"
-
-#define DEV_FORMAT "/dev/ttyUSB%d"
-
-USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_cp210x_buf[USB_ALIGN_UP(64, CONFIG_USB_ALIGN_SIZE)];
-
-#define CONFIG_USBHOST_MAX_CP210X_CLASS 1
-
-static struct usbh_cp210x g_cp210x_class[CONFIG_USBHOST_MAX_CP210X_CLASS];
-static uint32_t g_devinuse = 0;
-
-static struct usbh_cp210x *usbh_cp210x_class_alloc(void)
-{
- uint8_t devno;
-
- for (devno = 0; devno < CONFIG_USBHOST_MAX_CP210X_CLASS; devno++) {
- if ((g_devinuse & (1U << devno)) == 0) {
- g_devinuse |= (1U << devno);
- memset(&g_cp210x_class[devno], 0, sizeof(struct usbh_cp210x));
- g_cp210x_class[devno].minor = devno;
- return &g_cp210x_class[devno];
- }
- }
- return NULL;
-}
-
-static void usbh_cp210x_class_free(struct usbh_cp210x *cp210x_class)
-{
- uint8_t devno = cp210x_class->minor;
-
- if (devno < 32) {
- g_devinuse &= ~(1U << devno);
- }
- memset(cp210x_class, 0, sizeof(struct usbh_cp210x));
-}
-
-static int usbh_cp210x_enable(struct usbh_cp210x *cp210x_class)
-{
- struct usb_setup_packet *setup;
-
- if (!cp210x_class || !cp210x_class->hport) {
- return -USB_ERR_INVAL;
- }
- setup = cp210x_class->hport->setup;
-
- setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_INTERFACE;
- setup->bRequest = CP210X_IFC_ENABLE;
- setup->wValue = 1;
- setup->wIndex = cp210x_class->intf;
- setup->wLength = 0;
-
- return usbh_control_transfer(cp210x_class->hport, setup, NULL);
-}
-
-static int usbh_cp210x_set_flow(struct usbh_cp210x *cp210x_class)
-{
- struct usb_setup_packet *setup;
-
- if (!cp210x_class || !cp210x_class->hport) {
- return -USB_ERR_INVAL;
- }
- setup = cp210x_class->hport->setup;
-
- setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_INTERFACE;
- setup->bRequest = CP210X_SET_FLOW;
- setup->wValue = 0;
- setup->wIndex = cp210x_class->intf;
- setup->wLength = 16;
-
- memset(g_cp210x_buf, 0, 16);
- g_cp210x_buf[13] = 0x20;
- return usbh_control_transfer(cp210x_class->hport, setup, g_cp210x_buf);
-}
-
-static int usbh_cp210x_set_chars(struct usbh_cp210x *cp210x_class)
-{
- struct usb_setup_packet *setup;
-
- if (!cp210x_class || !cp210x_class->hport) {
- return -USB_ERR_INVAL;
- }
- setup = cp210x_class->hport->setup;
-
- setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_INTERFACE;
- setup->bRequest = CP210X_SET_CHARS;
- setup->wValue = 0;
- setup->wIndex = cp210x_class->intf;
- setup->wLength = 6;
-
- memset(g_cp210x_buf, 0, 6);
- g_cp210x_buf[0] = 0x80;
- g_cp210x_buf[4] = 0x88;
- g_cp210x_buf[5] = 0x28;
- return usbh_control_transfer(cp210x_class->hport, setup, g_cp210x_buf);
-}
-
-static int usbh_cp210x_set_baudrate(struct usbh_cp210x *cp210x_class, uint32_t baudrate)
-{
- struct usb_setup_packet *setup;
-
- if (!cp210x_class || !cp210x_class->hport) {
- return -USB_ERR_INVAL;
- }
- setup = cp210x_class->hport->setup;
-
- setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_INTERFACE;
- setup->bRequest = CP210X_SET_BAUDRATE;
- setup->wValue = 0;
- setup->wIndex = cp210x_class->intf;
- setup->wLength = 4;
-
- memcpy(g_cp210x_buf, (uint8_t *)&baudrate, 4);
- return usbh_control_transfer(cp210x_class->hport, setup, g_cp210x_buf);
-}
-
-static int usbh_cp210x_set_data_format(struct usbh_cp210x *cp210x_class, uint8_t databits, uint8_t parity, uint8_t stopbits)
-{
- struct usb_setup_packet *setup;
- uint16_t value;
-
- if (!cp210x_class || !cp210x_class->hport) {
- return -USB_ERR_INVAL;
- }
- setup = cp210x_class->hport->setup;
-
- value = ((databits & 0x0F) << 8) | ((parity & 0x0f) << 4) | ((stopbits & 0x03) << 0);
-
- setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_INTERFACE;
- setup->bRequest = CP210X_SET_LINE_CTL;
- setup->wValue = value;
- setup->wIndex = cp210x_class->intf;
- setup->wLength = 0;
-
- return usbh_control_transfer(cp210x_class->hport, setup, NULL);
-}
-
-static int usbh_cp210x_set_mhs(struct usbh_cp210x *cp210x_class, uint8_t dtr, uint8_t rts, uint8_t dtr_mask, uint8_t rts_mask)
-{
- struct usb_setup_packet *setup;
- uint16_t value;
-
- if (!cp210x_class || !cp210x_class->hport) {
- return -USB_ERR_INVAL;
- }
- setup = cp210x_class->hport->setup;
-
- value = ((dtr & 0x01) << 0) | ((rts & 0x01) << 1) | ((dtr_mask & 0x01) << 8) | ((rts_mask & 0x01) << 9);
-
- setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_INTERFACE;
- setup->bRequest = CP210X_SET_MHS;
- setup->wValue = value;
- setup->wIndex = cp210x_class->intf;
- setup->wLength = 0;
-
- return usbh_control_transfer(cp210x_class->hport, setup, NULL);
-}
-
-int usbh_cp210x_set_line_coding(struct usbh_cp210x *cp210x_class, struct cdc_line_coding *line_coding)
-{
- memcpy((uint8_t *)&cp210x_class->line_coding, line_coding, sizeof(struct cdc_line_coding));
- usbh_cp210x_set_baudrate(cp210x_class, line_coding->dwDTERate);
- return usbh_cp210x_set_data_format(cp210x_class, line_coding->bDataBits, line_coding->bParityType, line_coding->bCharFormat);
-}
-
-int usbh_cp210x_get_line_coding(struct usbh_cp210x *cp210x_class, struct cdc_line_coding *line_coding)
-{
- memcpy(line_coding, (uint8_t *)&cp210x_class->line_coding, sizeof(struct cdc_line_coding));
- return 0;
-}
-
-int usbh_cp210x_set_line_state(struct usbh_cp210x *cp210x_class, bool dtr, bool rts)
-{
- return usbh_cp210x_set_mhs(cp210x_class, dtr, rts, 1, 1);
-}
-
-static int usbh_cp210x_connect(struct usbh_hubport *hport, uint8_t intf)
-{
- struct usb_endpoint_descriptor *ep_desc;
- int ret = 0;
-
- struct usbh_cp210x *cp210x_class = usbh_cp210x_class_alloc();
- if (cp210x_class == NULL) {
- USB_LOG_ERR("Fail to alloc cp210x_class\r\n");
- return -USB_ERR_NOMEM;
- }
-
- cp210x_class->hport = hport;
- cp210x_class->intf = intf;
-
- hport->config.intf[intf].priv = cp210x_class;
-
- usbh_cp210x_enable(cp210x_class);
- usbh_cp210x_set_flow(cp210x_class);
- usbh_cp210x_set_chars(cp210x_class);
-
- for (uint8_t i = 0; i < hport->config.intf[intf].altsetting[0].intf_desc.bNumEndpoints; i++) {
- ep_desc = &hport->config.intf[intf].altsetting[0].ep[i].ep_desc;
-
- if (ep_desc->bEndpointAddress & 0x80) {
- USBH_EP_INIT(cp210x_class->bulkin, ep_desc);
- } else {
- USBH_EP_INIT(cp210x_class->bulkout, ep_desc);
- }
- }
-
- snprintf(hport->config.intf[intf].devname, CONFIG_USBHOST_DEV_NAMELEN, DEV_FORMAT, cp210x_class->minor);
-
- USB_LOG_INFO("Register CP210X Class:%s\r\n", hport->config.intf[intf].devname);
-
-#if 0
- USB_LOG_INFO("Test cp2102 rx and tx and rx for 5 times, baudrate is 115200\r\n");
-
- struct cdc_line_coding linecoding;
- uint8_t count = 5;
-
- linecoding.dwDTERate = 115200;
- linecoding.bDataBits = 8;
- linecoding.bParityType = 0;
- linecoding.bCharFormat = 0;
- usbh_cp210x_set_line_coding(cp210x_class, &linecoding);
- usbh_cp210x_set_line_state(cp210x_class, true, false);
-
- memset(g_cp210x_buf, 'a', sizeof(g_cp210x_buf));
- ret = usbh_cp210x_bulk_out_transfer(cp210x_class, g_cp210x_buf, sizeof(g_cp210x_buf), 0xfffffff);
- USB_LOG_RAW("out ret:%d\r\n", ret);
- while (count--) {
- ret = usbh_cp210x_bulk_in_transfer(cp210x_class, g_cp210x_buf, sizeof(g_cp210x_buf), 0xfffffff);
- USB_LOG_RAW("in ret:%d\r\n", ret);
- if (ret > 0) {
- for (uint32_t i = 0; i < ret; i++) {
- USB_LOG_RAW("%02x ", g_cp210x_buf[i]);
- }
- USB_LOG_RAW("\r\n");
- }
- }
-#endif
- usbh_cp210x_run(cp210x_class);
- return ret;
-}
-
-static int usbh_cp210x_disconnect(struct usbh_hubport *hport, uint8_t intf)
-{
- int ret = 0;
-
- struct usbh_cp210x *cp210x_class = (struct usbh_cp210x *)hport->config.intf[intf].priv;
-
- if (cp210x_class) {
- if (cp210x_class->bulkin) {
- usbh_kill_urb(&cp210x_class->bulkin_urb);
- }
-
- if (cp210x_class->bulkout) {
- usbh_kill_urb(&cp210x_class->bulkout_urb);
- }
-
- if (hport->config.intf[intf].devname[0] != '\0') {
- usb_osal_thread_schedule_other();
- USB_LOG_INFO("Unregister CP210X Class:%s\r\n", hport->config.intf[intf].devname);
- usbh_cp210x_stop(cp210x_class);
- }
-
- usbh_cp210x_class_free(cp210x_class);
- }
-
- return ret;
-}
-
-int usbh_cp210x_bulk_in_transfer(struct usbh_cp210x *cp210x_class, uint8_t *buffer, uint32_t buflen, uint32_t timeout)
-{
- int ret;
- struct usbh_urb *urb = &cp210x_class->bulkin_urb;
-
- usbh_bulk_urb_fill(urb, cp210x_class->hport, cp210x_class->bulkin, buffer, buflen, timeout, NULL, NULL);
- ret = usbh_submit_urb(urb);
- if (ret == 0) {
- ret = urb->actual_length;
- }
- return ret;
-}
-
-int usbh_cp210x_bulk_out_transfer(struct usbh_cp210x *cp210x_class, uint8_t *buffer, uint32_t buflen, uint32_t timeout)
-{
- int ret;
- struct usbh_urb *urb = &cp210x_class->bulkout_urb;
-
- usbh_bulk_urb_fill(urb, cp210x_class->hport, cp210x_class->bulkout, buffer, buflen, timeout, NULL, NULL);
- ret = usbh_submit_urb(urb);
- if (ret == 0) {
- ret = urb->actual_length;
- }
- return ret;
-}
-
-__WEAK void usbh_cp210x_run(struct usbh_cp210x *cp210x_class)
-{
- (void)cp210x_class;
-}
-
-__WEAK void usbh_cp210x_stop(struct usbh_cp210x *cp210x_class)
-{
- (void)cp210x_class;
-}
-
-static const uint16_t cp210x_id_table[][2] = {
- { 0x10C4, 0xEA60 },
- { 0, 0 },
-};
-
-const struct usbh_class_driver cp210x_class_driver = {
- .driver_name = "cp210x",
- .connect = usbh_cp210x_connect,
- .disconnect = usbh_cp210x_disconnect
-};
-
-CLASS_INFO_DEFINE const struct usbh_class_info cp210x_class_info = {
- .match_flags = USB_CLASS_MATCH_VID_PID | USB_CLASS_MATCH_INTF_CLASS,
- .bInterfaceClass = 0xff,
- .bInterfaceSubClass = 0x00,
- .bInterfaceProtocol = 0x00,
- .id_table = cp210x_id_table,
- .class_driver = &cp210x_class_driver
-}; \ No newline at end of file
diff --git a/class/vendor/serial/usbh_cp210x.h b/class/vendor/serial/usbh_cp210x.h
deleted file mode 100644
index d380c36c..00000000
--- a/class/vendor/serial/usbh_cp210x.h
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright (c) 2024, sakumisu
- *
- * SPDX-License-Identifier: Apache-2.0
- */
-#ifndef USBH_CP210X_H
-#define USBH_CP210X_H
-
-#include "usb_cdc.h"
-
-/* Requests */
-#define CP210X_IFC_ENABLE 0x00
-#define CP210X_SET_BAUDDIV 0x01
-#define CP210X_GET_BAUDDIV 0x02
-#define CP210X_SET_LINE_CTL 0x03 // Set parity, data bits, stop bits
-#define CP210X_GET_LINE_CTL 0x04
-#define CP210X_SET_BREAK 0x05
-#define CP210X_IMM_CHAR 0x06
-#define CP210X_SET_MHS 0x07 // Set DTR, RTS
-#define CP210X_GET_MDMSTS 0x08
-#define CP210X_SET_XON 0x09
-#define CP210X_SET_XOFF 0x0A
-#define CP210X_SET_EVENTMASK 0x0B
-#define CP210X_GET_EVENTMASK 0x0C
-#define CP210X_SET_CHAR 0x0D
-#define CP210X_GET_CHARS 0x0E
-#define CP210X_GET_PROPS 0x0F
-#define CP210X_GET_COMM_STATUS 0x10
-#define CP210X_RESET 0x11
-#define CP210X_PURGE 0x12
-#define CP210X_SET_FLOW 0x13
-#define CP210X_GET_FLOW 0x14
-#define CP210X_EMBED_EVENTS 0x15
-#define CP210X_GET_EVENTSTATE 0x16
-#define CP210X_SET_CHARS 0x19
-#define CP210X_GET_BAUDRATE 0x1D
-#define CP210X_SET_BAUDRATE 0x1E // Set baudrate
-#define CP210X_VENDOR_SPECIFIC 0xFF
-
-struct usbh_cp210x {
- struct usbh_hubport *hport;
- struct usb_endpoint_descriptor *bulkin; /* Bulk IN endpoint */
- struct usb_endpoint_descriptor *bulkout; /* Bulk OUT endpoint */
- struct usbh_urb bulkout_urb;
- struct usbh_urb bulkin_urb;
-
- struct cdc_line_coding line_coding;
-
- uint8_t intf;
- uint8_t minor;
-
- void *user_data;
-};
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-int usbh_cp210x_set_line_coding(struct usbh_cp210x *ftdi_class, struct cdc_line_coding *line_coding);
-int usbh_cp210x_get_line_coding(struct usbh_cp210x *ftdi_class, struct cdc_line_coding *line_coding);
-int usbh_cp210x_set_line_state(struct usbh_cp210x *ftdi_class, bool dtr, bool rts);
-
-int usbh_cp210x_bulk_in_transfer(struct usbh_cp210x *cp210x_class, uint8_t *buffer, uint32_t buflen, uint32_t timeout);
-int usbh_cp210x_bulk_out_transfer(struct usbh_cp210x *cp210x_class, uint8_t *buffer, uint32_t buflen, uint32_t timeout);
-
-void usbh_cp210x_run(struct usbh_cp210x *cp210x_class);
-void usbh_cp210x_stop(struct usbh_cp210x *cp210x_class);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* USBH_CP210X_H */
diff --git a/class/vendor/serial/usbh_ftdi.c b/class/vendor/serial/usbh_ftdi.c
deleted file mode 100644
index 4bc7f81b..00000000
--- a/class/vendor/serial/usbh_ftdi.c
+++ /dev/null
@@ -1,510 +0,0 @@
-/*
- * Copyright (c) 2024, sakumisu
- *
- * SPDX-License-Identifier: Apache-2.0
- */
-#include "usbh_core.h"
-#include "usbh_ftdi.h"
-
-#define DEV_FORMAT "/dev/ttyUSB%d"
-
-USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_ftdi_buf[USB_ALIGN_UP(64, CONFIG_USB_ALIGN_SIZE)];
-
-#define CONFIG_USBHOST_MAX_FTDI_CLASS 1
-
-static struct usbh_ftdi g_ftdi_class[CONFIG_USBHOST_MAX_FTDI_CLASS];
-static uint32_t g_devinuse = 0;
-
-static const char *ftdi_chip_name[] = {
- [SIO] = "SIO", /* the serial part of FT8U100AX */
- [FT232A] = "FT232A",
- [FT232B] = "FT232B",
- [FT2232C] = "FT2232C/D",
- [FT232R] = "FT232R",
- [FT232H] = "FT232H",
- [FT2232H] = "FT2232H",
- [FT4232H] = "FT4232H",
- [FT4232HA] = "FT4232HA",
- [FT232HP] = "FT232HP",
- [FT233HP] = "FT233HP",
- [FT2232HP] = "FT2232HP",
- [FT2233HP] = "FT2233HP",
- [FT4232HP] = "FT4232HP",
- [FT4233HP] = "FT4233HP",
- [FTX] = "FT-X",
-};
-
-static struct usbh_ftdi *usbh_ftdi_class_alloc(void)
-{
- uint8_t devno;
-
- for (devno = 0; devno < CONFIG_USBHOST_MAX_FTDI_CLASS; devno++) {
- if ((g_devinuse & (1U << devno)) == 0) {
- g_devinuse |= (1U << devno);
- memset(&g_ftdi_class[devno], 0, sizeof(struct usbh_ftdi));
- g_ftdi_class[devno].minor = devno;
- return &g_ftdi_class[devno];
- }
- }
- return NULL;
-}
-
-static void usbh_ftdi_class_free(struct usbh_ftdi *ftdi_class)
-{
- uint8_t devno = ftdi_class->minor;
-
- if (devno < 32) {
- g_devinuse &= ~(1U << devno);
- }
- memset(ftdi_class, 0, sizeof(struct usbh_ftdi));
-}
-
-/*
- * Divide positive or negative dividend by positive or negative divisor
- * and round to closest integer. Result is undefined for negative
- * divisors if the dividend variable type is unsigned and for negative
- * dividends if the divisor variable type is unsigned.
- */
-#define DIV_ROUND_CLOSEST(x, divisor) ( \
- { \
- typeof(x) __x = x; \
- typeof(divisor) __d = divisor; \
- (((typeof(x))-1) > 0 || \
- ((typeof(divisor))-1) > 0 || \
- (((__x) > 0) == ((__d) > 0))) ? \
- (((__x) + ((__d) / 2)) / (__d)) : \
- (((__x) - ((__d) / 2)) / (__d)); \
- })
-
-static uint32_t ftdi_232bm_baud_base_to_divisor(uint32_t baud, int base)
-{
- static const unsigned char divfrac[8] = { 0, 3, 2, 4, 1, 5, 6, 7 };
- uint32_t divisor;
- /* divisor shifted 3 bits to the left */
- int divisor3 = DIV_ROUND_CLOSEST(base, 2 * baud);
- divisor = divisor3 >> 3;
- divisor |= (uint32_t)divfrac[divisor3 & 0x7] << 14;
- /* Deal with special cases for highest baud rates. */
- if (divisor == 1) /* 1.0 */
- divisor = 0;
- else if (divisor == 0x4001) /* 1.5 */
- divisor = 1;
- return divisor;
-}
-
-static uint32_t ftdi_232bm_baud_to_divisor(uint32_t baud)
-{
- return ftdi_232bm_baud_base_to_divisor(baud, 48000000);
-}
-
-static uint32_t ftdi_2232h_baud_base_to_divisor(uint32_t baud, int base)
-{
- static const unsigned char divfrac[8] = { 0, 3, 2, 4, 1, 5, 6, 7 };
- uint32_t divisor;
- int divisor3;
-
- /* hi-speed baud rate is 10-bit sampling instead of 16-bit */
- divisor3 = DIV_ROUND_CLOSEST(8 * base, 10 * baud);
-
- divisor = divisor3 >> 3;
- divisor |= (uint32_t)divfrac[divisor3 & 0x7] << 14;
- /* Deal with special cases for highest baud rates. */
- if (divisor == 1) /* 1.0 */
- divisor = 0;
- else if (divisor == 0x4001) /* 1.5 */
- divisor = 1;
- /*
- * Set this bit to turn off a divide by 2.5 on baud rate generator
- * This enables baud rates up to 12Mbaud but cannot reach below 1200
- * baud with this bit set
- */
- divisor |= 0x00020000;
- return divisor;
-}
-
-static uint32_t ftdi_2232h_baud_to_divisor(uint32_t baud)
-{
- return ftdi_2232h_baud_base_to_divisor(baud, 120000000);
-}
-
-int usbh_ftdi_reset(struct usbh_ftdi *ftdi_class)
-{
- struct usb_setup_packet *setup;
-
- if (!ftdi_class || !ftdi_class->hport) {
- return -USB_ERR_INVAL;
- }
- setup = ftdi_class->hport->setup;
-
- setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE;
- setup->bRequest = SIO_RESET_REQUEST;
- setup->wValue = 0;
- setup->wIndex = ftdi_class->intf;
- setup->wLength = 0;
-
- return usbh_control_transfer(ftdi_class->hport, setup, NULL);
-}
-
-static int usbh_ftdi_set_modem(struct usbh_ftdi *ftdi_class, uint16_t value)
-{
- struct usb_setup_packet *setup;
-
- if (!ftdi_class || !ftdi_class->hport) {
- return -USB_ERR_INVAL;
- }
- setup = ftdi_class->hport->setup;
-
- setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE;
- setup->bRequest = SIO_SET_MODEM_CTRL_REQUEST;
- setup->wValue = value;
- setup->wIndex = ftdi_class->intf;
- setup->wLength = 0;
-
- return usbh_control_transfer(ftdi_class->hport, setup, NULL);
-}
-
-static int usbh_ftdi_set_baudrate(struct usbh_ftdi *ftdi_class, uint32_t baudrate)
-{
- struct usb_setup_packet *setup;
- uint32_t div_value;
- uint16_t value;
- uint8_t baudrate_high;
-
- if (!ftdi_class || !ftdi_class->hport) {
- return -USB_ERR_INVAL;
- }
- setup = ftdi_class->hport->setup;
-
- switch (ftdi_class->chip_type) {
- case FT232B:
- case FT2232C:
- case FT232R:
- if (baudrate > 3000000) {
- return -USB_ERR_INVAL;
- }
- div_value = ftdi_232bm_baud_to_divisor(baudrate);
- break;
- default:
- if ((baudrate <= 12000000) && (baudrate >= 1200)) {
- div_value = ftdi_2232h_baud_to_divisor(baudrate);
- } else {
- return -USB_ERR_INVAL;
- }
- break;
- }
-
- value = div_value & 0xFFFF;
- baudrate_high = (div_value >> 16) & 0xff;
-
- setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE;
- setup->bRequest = SIO_SET_BAUDRATE_REQUEST;
- setup->wValue = value;
- setup->wIndex = (baudrate_high << 8) | ftdi_class->intf;
- setup->wLength = 0;
-
- return usbh_control_transfer(ftdi_class->hport, setup, NULL);
-}
-
-static int usbh_ftdi_set_data_format(struct usbh_ftdi *ftdi_class, uint8_t databits, uint8_t parity, uint8_t stopbits, uint8_t isbreak)
-{
- /**
- * D0-D7 databits BITS_7=7, BITS_8=8
- * D8-D10 parity NONE=0, ODD=1, EVEN=2, MARK=3, SPACE=4
- * D11-D12 STOP_BIT_1=0, STOP_BIT_15=1, STOP_BIT_2=2
- * D14 BREAK_OFF=0, BREAK_ON=1
- **/
- struct usb_setup_packet *setup;
- uint16_t value;
-
- if (!ftdi_class || !ftdi_class->hport) {
- return -USB_ERR_INVAL;
- }
- setup = ftdi_class->hport->setup;
-
- value = ((isbreak & 0x01) << 14) | ((stopbits & 0x03) << 11) | ((parity & 0x0f) << 8) | (databits & 0x0f);
-
- setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE;
- setup->bRequest = SIO_SET_DATA_REQUEST;
- setup->wValue = value;
- setup->wIndex = ftdi_class->intf;
- setup->wLength = 0;
-
- return usbh_control_transfer(ftdi_class->hport, setup, NULL);
-}
-
-static int usbh_ftdi_set_latency_timer(struct usbh_ftdi *ftdi_class, uint16_t value)
-{
- struct usb_setup_packet *setup;
-
- if (!ftdi_class || !ftdi_class->hport) {
- return -USB_ERR_INVAL;
- }
- setup = ftdi_class->hport->setup;
-
- setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE;
- setup->bRequest = SIO_SET_LATENCY_TIMER_REQUEST;
- setup->wValue = value;
- setup->wIndex = ftdi_class->intf;
- setup->wLength = 0;
-
- return usbh_control_transfer(ftdi_class->hport, setup, NULL);
-}
-
-static int usbh_ftdi_set_flow_ctrl(struct usbh_ftdi *ftdi_class, uint16_t value)
-{
- struct usb_setup_packet *setup;
-
- if (!ftdi_class || !ftdi_class->hport) {
- return -USB_ERR_INVAL;
- }
- setup = ftdi_class->hport->setup;
-
- setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE;
- setup->bRequest = SIO_SET_FLOW_CTRL_REQUEST;
- setup->wValue = value;
- setup->wIndex = ftdi_class->intf;
- setup->wLength = 0;
-
- return usbh_control_transfer(ftdi_class->hport, setup, NULL);
-}
-
-static int usbh_ftdi_read_modem_status(struct usbh_ftdi *ftdi_class)
-{
- struct usb_setup_packet *setup;
- int ret;
-
- if (!ftdi_class || !ftdi_class->hport) {
- return -USB_ERR_INVAL;
- }
- setup = ftdi_class->hport->setup;
-
- setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE;
- setup->bRequest = SIO_POLL_MODEM_STATUS_REQUEST;
- setup->wValue = 0x0000;
- setup->wIndex = ftdi_class->intf;
- setup->wLength = 2;
-
- ret = usbh_control_transfer(ftdi_class->hport, setup, g_ftdi_buf);
- if (ret < 0) {
- return ret;
- }
- memcpy(ftdi_class->modem_status, g_ftdi_buf, 2);
- return ret;
-}
-
-int usbh_ftdi_set_line_coding(struct usbh_ftdi *ftdi_class, struct cdc_line_coding *line_coding)
-{
- memcpy((uint8_t *)&ftdi_class->line_coding, line_coding, sizeof(struct cdc_line_coding));
-
- int ret = usbh_ftdi_set_baudrate(ftdi_class, line_coding->dwDTERate);
- if (ret < 0) {
- return ret;
- }
- return usbh_ftdi_set_data_format(ftdi_class, line_coding->bDataBits, line_coding->bParityType, line_coding->bCharFormat, 0);
-}
-
-int usbh_ftdi_get_line_coding(struct usbh_ftdi *ftdi_class, struct cdc_line_coding *line_coding)
-{
- memcpy(line_coding, (uint8_t *)&ftdi_class->line_coding, sizeof(struct cdc_line_coding));
- return 0;
-}
-
-int usbh_ftdi_set_line_state(struct usbh_ftdi *ftdi_class, bool dtr, bool rts)
-{
- int ret;
-
- if (dtr) {
- usbh_ftdi_set_modem(ftdi_class, SIO_SET_DTR_HIGH);
- } else {
- usbh_ftdi_set_modem(ftdi_class, SIO_SET_DTR_LOW);
- }
-
- if (rts) {
- ret = usbh_ftdi_set_modem(ftdi_class, SIO_SET_RTS_HIGH);
- } else {
- ret = usbh_ftdi_set_modem(ftdi_class, SIO_SET_RTS_LOW);
- }
-
- return ret;
-}
-
-static int usbh_ftdi_connect(struct usbh_hubport *hport, uint8_t intf)
-{
- struct usb_endpoint_descriptor *ep_desc;
- int ret = 0;
- uint16_t version;
-
- struct usbh_ftdi *ftdi_class = usbh_ftdi_class_alloc();
- if (ftdi_class == NULL) {
- USB_LOG_ERR("Fail to alloc ftdi_class\r\n");
- return -USB_ERR_NOMEM;
- }
-
- ftdi_class->hport = hport;
- ftdi_class->intf = intf;
-
- hport->config.intf[intf].priv = ftdi_class;
-
- version = hport->device_desc.bcdDevice;
-
- switch (version) {
- case 0x400:
- ftdi_class->chip_type = FT232B;
- break;
- case 0x500:
- ftdi_class->chip_type = FT2232C;
- break;
- case 0x600:
- ftdi_class->chip_type = FT232R;
- break;
- case 0x700:
- ftdi_class->chip_type = FT2232H;
- break;
- case 0x800:
- ftdi_class->chip_type = FT4232H;
- break;
- case 0x900:
- ftdi_class->chip_type = FT232H;
- break;
-
- default:
- USB_LOG_ERR("Unknown FTDI chip version:%04x\r\n", version);
- return -USB_ERR_NOTSUPP;
- }
-
- USB_LOG_INFO("FTDI chip name:%s\r\n", ftdi_chip_name[ftdi_class->chip_type]);
-
- usbh_ftdi_reset(ftdi_class);
- usbh_ftdi_set_flow_ctrl(ftdi_class, SIO_DISABLE_FLOW_CTRL);
- usbh_ftdi_set_latency_timer(ftdi_class, 0x10);
- usbh_ftdi_read_modem_status(ftdi_class);
- USB_LOG_INFO("modem status:%02x:%02x\r\n", ftdi_class->modem_status[0], ftdi_class->modem_status[1]);
-
- for (uint8_t i = 0; i < hport->config.intf[intf].altsetting[0].intf_desc.bNumEndpoints; i++) {
- ep_desc = &hport->config.intf[intf].altsetting[0].ep[i].ep_desc;
-
- if (ep_desc->bEndpointAddress & 0x80) {
- USBH_EP_INIT(ftdi_class->bulkin, ep_desc);
- } else {
- USBH_EP_INIT(ftdi_class->bulkout, ep_desc);
- }
- }
-
- snprintf(hport->config.intf[intf].devname, CONFIG_USBHOST_DEV_NAMELEN, DEV_FORMAT, ftdi_class->minor);
-
- USB_LOG_INFO("Register FTDI Class:%s\r\n", hport->config.intf[intf].devname);
-
-#if 0
- USB_LOG_INFO("Test ftdi rx and tx and rx for 5 times, baudrate is 115200\r\n");
-
- struct cdc_line_coding linecoding;
- uint8_t count = 5;
-
- linecoding.dwDTERate = 115200;
- linecoding.bDataBits = 8;
- linecoding.bParityType = 0;
- linecoding.bCharFormat = 0;
- usbh_ftdi_set_line_coding(ftdi_class, &linecoding);
- usbh_ftdi_set_line_state(ftdi_class, true, false);
-
- memset(g_ftdi_buf, 'a', sizeof(g_ftdi_buf));
- ret = usbh_ftdi_bulk_out_transfer(ftdi_class, g_ftdi_buf, sizeof(g_ftdi_buf), 0xfffffff);
- USB_LOG_RAW("out ret:%d\r\n", ret);
- while (count--) {
- ret = usbh_ftdi_bulk_in_transfer(ftdi_class, g_ftdi_buf, sizeof(g_ftdi_buf), 0xfffffff);
- USB_LOG_RAW("in ret:%d\r\n", ret);
- if (ret > 0) {
- for (uint32_t i = 0; i < ret; i++) {
- USB_LOG_RAW("%02x ", g_ftdi_buf[i]);
- }
- }
- USB_LOG_RAW("\r\n");
- }
-#endif
- usbh_ftdi_run(ftdi_class);
- return ret;
-}
-
-static int usbh_ftdi_disconnect(struct usbh_hubport *hport, uint8_t intf)
-{
- int ret = 0;
-
- struct usbh_ftdi *ftdi_class = (struct usbh_ftdi *)hport->config.intf[intf].priv;
-
- if (ftdi_class) {
- if (ftdi_class->bulkin) {
- usbh_kill_urb(&ftdi_class->bulkin_urb);
- }
-
- if (ftdi_class->bulkout) {
- usbh_kill_urb(&ftdi_class->bulkout_urb);
- }
-
- if (hport->config.intf[intf].devname[0] != '\0') {
- usb_osal_thread_schedule_other();
- USB_LOG_INFO("Unregister FTDI Class:%s\r\n", hport->config.intf[intf].devname);
- usbh_ftdi_stop(ftdi_class);
- }
-
- usbh_ftdi_class_free(ftdi_class);
- }
-
- return ret;
-}
-
-int usbh_ftdi_bulk_in_transfer(struct usbh_ftdi *ftdi_class, uint8_t *buffer, uint32_t buflen, uint32_t timeout)
-{
- int ret;
- struct usbh_urb *urb = &ftdi_class->bulkin_urb;
-
- usbh_bulk_urb_fill(urb, ftdi_class->hport, ftdi_class->bulkin, buffer, buflen, timeout, NULL, NULL);
- ret = usbh_submit_urb(urb);
- if (ret == 0) {
- ret = urb->actual_length;
- }
- return ret;
-}
-
-int usbh_ftdi_bulk_out_transfer(struct usbh_ftdi *ftdi_class, uint8_t *buffer, uint32_t buflen, uint32_t timeout)
-{
- int ret;
- struct usbh_urb *urb = &ftdi_class->bulkout_urb;
-
- usbh_bulk_urb_fill(urb, ftdi_class->hport, ftdi_class->bulkout, buffer, buflen, timeout, NULL, NULL);
- ret = usbh_submit_urb(urb);
- if (ret == 0) {
- ret = urb->actual_length;
- }
- return ret;
-}
-
-__WEAK void usbh_ftdi_run(struct usbh_ftdi *ftdi_class)
-{
- (void)ftdi_class;
-}
-
-__WEAK void usbh_ftdi_stop(struct usbh_ftdi *ftdi_class)
-{
- (void)ftdi_class;
-}
-
-static const uint16_t ftdi_id_table[][2] = {
- { 0x0403, 0x6001 },
- { 0x0403, 0x6010 },
- { 0, 0 },
-};
-
-const struct usbh_class_driver ftdi_class_driver = {
- .driver_name = "ftdi",
- .connect = usbh_ftdi_connect,
- .disconnect = usbh_ftdi_disconnect
-};
-
-CLASS_INFO_DEFINE const struct usbh_class_info ftdi_class_info = {
- .match_flags = USB_CLASS_MATCH_VID_PID | USB_CLASS_MATCH_INTF_CLASS,
- .bInterfaceClass = 0xff,
- .bInterfaceSubClass = 0x00,
- .bInterfaceProtocol = 0x00,
- .id_table = ftdi_id_table,
- .class_driver = &ftdi_class_driver
-}; \ No newline at end of file
diff --git a/class/vendor/serial/usbh_ftdi.h b/class/vendor/serial/usbh_ftdi.h
deleted file mode 100644
index 855db8d6..00000000
--- a/class/vendor/serial/usbh_ftdi.h
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Copyright (c) 2024, sakumisu
- *
- * SPDX-License-Identifier: Apache-2.0
- */
-#ifndef USBH_FTDI_H
-#define USBH_FTDI_H
-
-#include "usb_cdc.h"
-
-/* Requests */
-#define SIO_RESET_REQUEST 0x00 /* Reset the port */
-#define SIO_SET_MODEM_CTRL_REQUEST 0x01 /* Set the modem control register */
-#define SIO_SET_FLOW_CTRL_REQUEST 0x02 /* Set flow control register */
-#define SIO_SET_BAUDRATE_REQUEST 0x03 /* Set baud rate */
-#define SIO_SET_DATA_REQUEST 0x04 /* Set the data characteristics of the port */
-#define SIO_POLL_MODEM_STATUS_REQUEST 0x05
-#define SIO_SET_EVENT_CHAR_REQUEST 0x06
-#define SIO_SET_ERROR_CHAR_REQUEST 0x07
-#define SIO_SET_LATENCY_TIMER_REQUEST 0x09
-#define SIO_GET_LATENCY_TIMER_REQUEST 0x0A
-#define SIO_SET_BITMODE_REQUEST 0x0B
-#define SIO_READ_PINS_REQUEST 0x0C
-#define SIO_READ_EEPROM_REQUEST 0x90
-#define SIO_WRITE_EEPROM_REQUEST 0x91
-#define SIO_ERASE_EEPROM_REQUEST 0x92
-
-#define SIO_DISABLE_FLOW_CTRL 0x0
-#define SIO_RTS_CTS_HS (0x1 << 8)
-#define SIO_DTR_DSR_HS (0x2 << 8)
-#define SIO_XON_XOFF_HS (0x4 << 8)
-
-#define SIO_SET_DTR_MASK 0x1
-#define SIO_SET_DTR_HIGH (1 | (SIO_SET_DTR_MASK << 8))
-#define SIO_SET_DTR_LOW (0 | (SIO_SET_DTR_MASK << 8))
-#define SIO_SET_RTS_MASK 0x2
-#define SIO_SET_RTS_HIGH (2 | (SIO_SET_RTS_MASK << 8))
-#define SIO_SET_RTS_LOW (0 | (SIO_SET_RTS_MASK << 8))
-
-#define SIO_RTS_CTS_HS (0x1 << 8)
-
-enum ftdi_chip_type {
- SIO,
- FT232A,
- FT232B,
- FT2232C,
- FT232R,
- FT232H,
- FT2232H,
- FT4232H,
- FT4232HA,
- FT232HP,
- FT233HP,
- FT2232HP,
- FT2233HP,
- FT4232HP,
- FT4233HP,
- FTX,
-};
-
-struct usbh_ftdi {
- struct usbh_hubport *hport;
- struct usb_endpoint_descriptor *bulkin; /* Bulk IN endpoint */
- struct usb_endpoint_descriptor *bulkout; /* Bulk OUT endpoint */
- struct usbh_urb bulkout_urb;
- struct usbh_urb bulkin_urb;
-
- struct cdc_line_coding line_coding;
-
- uint8_t intf;
- uint8_t minor;
- uint8_t modem_status[2];
- enum ftdi_chip_type chip_type;
-
- void *user_data;
-};
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-int usbh_ftdi_set_line_coding(struct usbh_ftdi *ftdi_class, struct cdc_line_coding *line_coding);
-int usbh_ftdi_get_line_coding(struct usbh_ftdi *ftdi_class, struct cdc_line_coding *line_coding);
-int usbh_ftdi_set_line_state(struct usbh_ftdi *ftdi_class, bool dtr, bool rts);
-
-int usbh_ftdi_bulk_in_transfer(struct usbh_ftdi *ftdi_class, uint8_t *buffer, uint32_t buflen, uint32_t timeout);
-int usbh_ftdi_bulk_out_transfer(struct usbh_ftdi *ftdi_class, uint8_t *buffer, uint32_t buflen, uint32_t timeout);
-
-void usbh_ftdi_run(struct usbh_ftdi *ftdi_class);
-void usbh_ftdi_stop(struct usbh_ftdi *ftdi_class);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* USBH_FTDI_H */
diff --git a/class/vendor/serial/usbh_pl2303.c b/class/vendor/serial/usbh_pl2303.c
deleted file mode 100644
index ece32538..00000000
--- a/class/vendor/serial/usbh_pl2303.c
+++ /dev/null
@@ -1,449 +0,0 @@
-/*
- * Copyright (c) 2024, sakumisu
- * Copyright (c) 2024, Derek Konigsberg
- *
- * SPDX-License-Identifier: Apache-2.0
- */
-#include "usbh_core.h"
-#include "usbh_pl2303.h"
-
-#undef USB_DBG_TAG
-#define USB_DBG_TAG "usbh_pl2303"
-#include "usb_log.h"
-
-#define DEV_FORMAT "/dev/ttyUSB%d"
-
-USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_pl2303_buf[USB_ALIGN_UP(64, CONFIG_USB_ALIGN_SIZE)];
-
-#define CONFIG_USBHOST_MAX_PL2303_CLASS 1
-
-#define UT_WRITE_VENDOR_DEVICE (USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE)
-#define UT_READ_VENDOR_DEVICE (USB_REQUEST_DIR_IN | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE)
-
-static struct usbh_pl2303 g_pl2303_class[CONFIG_USBHOST_MAX_PL2303_CLASS];
-static uint32_t g_devinuse = 0;
-
-static struct usbh_pl2303 *usbh_pl2303_class_alloc(void)
-{
- uint8_t devno;
-
- for (devno = 0; devno < CONFIG_USBHOST_MAX_PL2303_CLASS; devno++) {
- if ((g_devinuse & (1U << devno)) == 0) {
- g_devinuse |= (1U << devno);
- memset(&g_pl2303_class[devno], 0, sizeof(struct usbh_pl2303));
- g_pl2303_class[devno].minor = devno;
- return &g_pl2303_class[devno];
- }
- }
- return NULL;
-}
-
-static void usbh_pl2303_class_free(struct usbh_pl2303 *pl2303_class)
-{
- uint8_t devno = pl2303_class->minor;
-
- if (devno < 32) {
- g_devinuse &= ~(1U << devno);
- }
- memset(pl2303_class, 0, sizeof(struct usbh_pl2303));
-}
-
-static int usbh_pl2303_get_chiptype(struct usbh_pl2303 *pl2303_class)
-{
- int ret = 0;
-
- switch (pl2303_class->hport->device_desc.bcdDevice) {
- case 0x0300:
- pl2303_class->chiptype = USBH_PL2303_TYPE_PL2303HX;
- /* or TA, that is HX with external crystal */
- break;
- case 0x0400:
- pl2303_class->chiptype = USBH_PL2303_TYPE_PL2303HXD;
- /* or EA, that is HXD with ESD protection */
- /* or RA, that has internal voltage level converter that works only up to 1Mbaud (!) */
- break;
- case 0x0500:
- pl2303_class->chiptype = USBH_PL2303_TYPE_PL2303HXD;
- /* in fact it's TB, that is HXD with external crystal */
- break;
- default:
- /* NOTE: I have no info about the bcdDevice for the base PL2303 (up to 1.2Mbaud,
- only fixed rates) and for PL2303SA (8-pin chip, up to 115200 baud */
- /* Determine the chip type. This algorithm is taken from Linux. */
- if (pl2303_class->hport->device_desc.bDeviceClass == 0x02) {
- pl2303_class->chiptype = USBH_PL2303_TYPE_PL2303;
- } else if (pl2303_class->hport->device_desc.bMaxPacketSize0 == 0x40) {
- pl2303_class->chiptype = USBH_PL2303_TYPE_PL2303HX;
- } else {
- pl2303_class->chiptype = USBH_PL2303_TYPE_PL2303;
- }
- break;
- }
-
- /*
- * The new chip revision PL2303HXN is only compatible with the new
- * PLCOM_SET_REQUEST_PL2303HXN command. Issuing the old command
- * PLCOM_SET_REQUEST to the new chip raises an error. Thus, PL2303HX
- * and PL2303HXN can be distinguished by issuing an old-style request
- * (on a status register) to the new chip and checking the error.
- */
- if (pl2303_class->chiptype == USBH_PL2303_TYPE_PL2303HX) {
- struct usb_setup_packet *setup = pl2303_class->hport->setup;
-
- setup->bmRequestType = UT_READ_VENDOR_DEVICE;
- setup->bRequest = PL2303_SET_REQUEST;
- setup->wValue = PL2303_STATUS_REG_PL2303HX;
- setup->wIndex = 0;
- setup->wLength = 1;
-
- ret = usbh_control_transfer(pl2303_class->hport, setup, g_pl2303_buf);
- if (ret == -USB_ERR_STALL) {
- pl2303_class->chiptype = USBH_PL2303_TYPE_PL2303HXN;
- ret = 0;
- } else if (ret < 0) {
- USB_LOG_WRN("Error checking chip type: %d\r\n", ret);
- return ret;
- }
- }
-
- switch (pl2303_class->chiptype) {
- case USBH_PL2303_TYPE_PL2303:
- USB_LOG_INFO("chiptype = 2303\r\n");
- break;
- case USBH_PL2303_TYPE_PL2303HX:
- USB_LOG_INFO("chiptype = 2303HX/TA\r\n");
- break;
- case USBH_PL2303_TYPE_PL2303HXN:
- USB_LOG_INFO("chiptype = 2303HXN\r\n");
- break;
- case USBH_PL2303_TYPE_PL2303HXD:
- USB_LOG_INFO("chiptype = 2303HXD/TB/RA/EA\r\n");
- break;
- default:
- USB_LOG_INFO("chiptype = [%d]\r\n", pl2303_class->chiptype);
- break;
- }
-
- return ret;
-}
-
-static int usbh_pl2303_do(struct usbh_pl2303 *pl2303_class,
- uint8_t req_type, uint8_t request, uint16_t value, uint16_t index,
- uint16_t length)
-{
- struct usb_setup_packet *setup;
-
- if (!pl2303_class || !pl2303_class->hport) {
- return -USB_ERR_INVAL;
- }
- setup = pl2303_class->hport->setup;
-
- setup->bmRequestType = req_type;
- setup->bRequest = request;
- setup->wValue = value;
- setup->wIndex = index;
- setup->wLength = length;
-
- return usbh_control_transfer(pl2303_class->hport, setup, g_pl2303_buf);
-}
-
-int usbh_pl2303_set_line_coding(struct usbh_pl2303 *pl2303_class, struct cdc_line_coding *line_coding)
-{
- struct usb_setup_packet *setup;
-
- if (!pl2303_class || !pl2303_class->hport) {
- return -USB_ERR_INVAL;
- }
- setup = pl2303_class->hport->setup;
-
- setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE;
- setup->bRequest = CDC_REQUEST_SET_LINE_CODING;
- setup->wValue = 0;
- setup->wIndex = pl2303_class->intf;
- setup->wLength = 7;
-
- memcpy(g_pl2303_buf, line_coding, sizeof(struct cdc_line_coding));
-
- return usbh_control_transfer(pl2303_class->hport, setup, g_pl2303_buf);
-}
-
-int usbh_pl2303_get_line_coding(struct usbh_pl2303 *pl2303_class, struct cdc_line_coding *line_coding)
-{
- struct usb_setup_packet *setup;
- int ret;
-
- if (!pl2303_class || !pl2303_class->hport) {
- return -USB_ERR_INVAL;
- }
- setup = pl2303_class->hport->setup;
-
- setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE;
- setup->bRequest = CDC_REQUEST_GET_LINE_CODING;
- setup->wValue = 0;
- setup->wIndex = pl2303_class->intf;
- setup->wLength = 7;
-
- ret = usbh_control_transfer(pl2303_class->hport, setup, g_pl2303_buf);
- if (ret < 0) {
- return ret;
- }
- memcpy(line_coding, g_pl2303_buf, sizeof(struct cdc_line_coding));
- return ret;
-}
-
-int usbh_pl2303_set_line_state(struct usbh_pl2303 *pl2303_class, bool dtr, bool rts)
-{
- struct usb_setup_packet *setup;
-
- if (!pl2303_class || !pl2303_class->hport) {
- return -USB_ERR_INVAL;
- }
- setup = pl2303_class->hport->setup;
-
- setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE;
- setup->bRequest = CDC_REQUEST_SET_CONTROL_LINE_STATE;
- setup->wValue = (dtr << 0) | (rts << 1);
- setup->wIndex = pl2303_class->intf;
- setup->wLength = 0;
-
- return usbh_control_transfer(pl2303_class->hport, setup, NULL);
-}
-
-static int usbh_pl2303_connect(struct usbh_hubport *hport, uint8_t intf)
-{
- struct usb_endpoint_descriptor *ep_desc;
- int ret = 0;
-
- struct usbh_pl2303 *pl2303_class = usbh_pl2303_class_alloc();
- if (pl2303_class == NULL) {
- USB_LOG_ERR("Fail to alloc pl2303_class\r\n");
- return -USB_ERR_NOMEM;
- }
-
- pl2303_class->hport = hport;
- pl2303_class->intf = intf;
-
- hport->config.intf[intf].priv = pl2303_class;
-
- do {
- ret = usbh_pl2303_get_chiptype(pl2303_class);
- if (ret < 0) {
- break;
- }
-
- /* Startup reset sequence, if necessary for the chip type */
- if (pl2303_class->chiptype != USBH_PL2303_TYPE_PL2303HXN) {
- struct usb_setup_packet *setup = pl2303_class->hport->setup;
-
- setup->bmRequestType = UT_WRITE_VENDOR_DEVICE;
- setup->bRequest = PL2303_SET_REQUEST;
- setup->wValue = 0;
- setup->wIndex = pl2303_class->intf;
- setup->wLength = 0;
-
- ret = usbh_control_transfer(pl2303_class->hport, setup, g_pl2303_buf);
- if (ret < 0) {
- USB_LOG_WRN("Initialization reset failed: %d\r\n", ret);
- break;
- }
- }
-
- if (pl2303_class->chiptype == USBH_PL2303_TYPE_PL2303) {
- /* HX variants seem to lock up after a clear stall request. */
- /*
- * The FreeBSD code sets the stall flags on the in and out pipes
- * here. Have no idea exactly how to do this, or if it is necessary.
- * May just leave this code unwritten until test hardware is available.
- */
- } else if (pl2303_class->chiptype == USBH_PL2303_TYPE_PL2303HX || pl2303_class->chiptype == USBH_PL2303_TYPE_PL2303HXD) {
- /* Reset upstream data pipes */
- ret = usbh_pl2303_do(pl2303_class, UT_WRITE_VENDOR_DEVICE, PL2303_SET_REQUEST, 8, 0, 0);
- if (ret < 0) {
- USB_LOG_WRN("Could not reset upstream data pipes (8,0): %d\r\n", ret);
- break;
- }
- ret = usbh_pl2303_do(pl2303_class, UT_WRITE_VENDOR_DEVICE, PL2303_SET_REQUEST, 9, 0, 0);
- if (ret < 0) {
- USB_LOG_WRN("Could not reset upstream data pipes (9,0): %d\r\n", ret);
- break;
- }
- } else if (pl2303_class->chiptype == USBH_PL2303_TYPE_PL2303HXN) {
- /* Reset upstream data pipes */
- ret = usbh_pl2303_do(pl2303_class, UT_WRITE_VENDOR_DEVICE, PL2303_SET_REQUEST_PL2303HXN, 0x07, 0x03, 0);
- if (ret < 0) {
- USB_LOG_WRN("Could not reset upstream data pipes (7,3): %d\r\n", ret);
- break;
- }
- }
-
- /* Final device initialization, if necessary for the chip type */
- if (pl2303_class->chiptype != USBH_PL2303_TYPE_PL2303HXN) {
- if (usbh_pl2303_do(pl2303_class, UT_READ_VENDOR_DEVICE, PL2303_SET_REQUEST, 0x8484, 0, 1) < 0 ||
- usbh_pl2303_do(pl2303_class, UT_WRITE_VENDOR_DEVICE, PL2303_SET_REQUEST, 0x0404, 0, 0) < 0 ||
- usbh_pl2303_do(pl2303_class, UT_READ_VENDOR_DEVICE, PL2303_SET_REQUEST, 0x8484, 0, 1) < 0 ||
- usbh_pl2303_do(pl2303_class, UT_READ_VENDOR_DEVICE, PL2303_SET_REQUEST, 0x8383, 0, 1) < 0 ||
- usbh_pl2303_do(pl2303_class, UT_READ_VENDOR_DEVICE, PL2303_SET_REQUEST, 0x8484, 0, 1) < 0 ||
- usbh_pl2303_do(pl2303_class, UT_WRITE_VENDOR_DEVICE, PL2303_SET_REQUEST, 0x0404, 1, 0) < 0 ||
- usbh_pl2303_do(pl2303_class, UT_READ_VENDOR_DEVICE, PL2303_SET_REQUEST, 0x8484, 0, 1) < 0 ||
- usbh_pl2303_do(pl2303_class, UT_READ_VENDOR_DEVICE, PL2303_SET_REQUEST, 0x8383, 0, 1) < 0 ||
- usbh_pl2303_do(pl2303_class, UT_WRITE_VENDOR_DEVICE, PL2303_SET_REQUEST, 0, 1, 0) < 0 ||
- usbh_pl2303_do(pl2303_class, UT_WRITE_VENDOR_DEVICE, PL2303_SET_REQUEST, 1, 0, 0) < 0) {
- USB_LOG_WRN("Could not complete init sequence\r\n");
- ret = -USB_ERR_INVAL;
- break;
- }
-
- if (pl2303_class->chiptype != USBH_PL2303_TYPE_PL2303) {
- ret = usbh_pl2303_do(pl2303_class, UT_WRITE_VENDOR_DEVICE, PL2303_SET_REQUEST, 2, 0x44, 0);
- } else {
- ret = usbh_pl2303_do(pl2303_class, UT_WRITE_VENDOR_DEVICE, PL2303_SET_REQUEST, 2, 0x24, 0);
- }
- if (ret < 0) {
- USB_LOG_WRN("Could not complete final init request: %d\r\n", ret);
- break;
- }
- }
- } while (0);
-
- if (ret < 0) {
- USB_LOG_ERR("Failed to initialize PL2303 device: %d\r\n", ret);
- return ret;
- }
-
- for (uint8_t i = 0; i < hport->config.intf[intf].altsetting[0].intf_desc.bNumEndpoints; i++) {
- ep_desc = &hport->config.intf[intf].altsetting[0].ep[i].ep_desc;
- if (USB_GET_ENDPOINT_TYPE(ep_desc->bmAttributes) == USB_ENDPOINT_TYPE_INTERRUPT) {
- continue;
- } else {
- if (ep_desc->bEndpointAddress & 0x80) {
- USBH_EP_INIT(pl2303_class->bulkin, ep_desc);
- } else {
- USBH_EP_INIT(pl2303_class->bulkout, ep_desc);
- }
- }
- }
-
- snprintf(hport->config.intf[intf].devname, CONFIG_USBHOST_DEV_NAMELEN, DEV_FORMAT, pl2303_class->minor);
-
- USB_LOG_INFO("Register PL2303 Class:%s\r\n", hport->config.intf[intf].devname);
-
-#if 0
- USB_LOG_INFO("Test pl2303 rx and tx and rx for 5 times, baudrate is 115200\r\n");
-
- struct cdc_line_coding linecoding;
- uint8_t count = 5;
-
- linecoding.dwDTERate = 115200;
- linecoding.bDataBits = 8;
- linecoding.bParityType = 0;
- linecoding.bCharFormat = 0;
- usbh_pl2303_set_line_coding(pl2303_class, &linecoding);
- usbh_pl2303_set_line_state(pl2303_class, true, false);
-
- memset(g_pl2303_buf, 'a', sizeof(g_pl2303_buf));
- ret = usbh_pl2303_bulk_out_transfer(pl2303_class, g_pl2303_buf, sizeof(g_pl2303_buf), 0xfffffff);
- USB_LOG_RAW("out ret:%d\r\n", ret);
- while (count--) {
- ret = usbh_pl2303_bulk_in_transfer(pl2303_class, g_pl2303_buf, sizeof(g_pl2303_buf), 0xfffffff);
- USB_LOG_RAW("in ret:%d\r\n", ret);
- if (ret > 0) {
- for (uint32_t i = 0; i < ret; i++) {
- USB_LOG_RAW("%02x ", g_pl2303_buf[i]);
- }
- }
- USB_LOG_RAW("\r\n");
- }
-#endif
-
- usbh_pl2303_run(pl2303_class);
- return ret;
-}
-
-static int usbh_pl2303_disconnect(struct usbh_hubport *hport, uint8_t intf)
-{
- int ret = 0;
-
- struct usbh_pl2303 *pl2303_class = (struct usbh_pl2303 *)hport->config.intf[intf].priv;
-
- if (pl2303_class) {
- if (pl2303_class->bulkin) {
- usbh_kill_urb(&pl2303_class->bulkin_urb);
- }
-
- if (pl2303_class->bulkout) {
- usbh_kill_urb(&pl2303_class->bulkout_urb);
- }
-
- if (hport->config.intf[intf].devname[0] != '\0') {
- usb_osal_thread_schedule_other();
- USB_LOG_INFO("Unregister PL2303 Class:%s\r\n", hport->config.intf[intf].devname);
- usbh_pl2303_stop(pl2303_class);
- }
-
- usbh_pl2303_class_free(pl2303_class);
- }
-
- return ret;
-}
-
-int usbh_pl2303_bulk_in_transfer(struct usbh_pl2303 *pl2303_class, uint8_t *buffer, uint32_t buflen, uint32_t timeout)
-{
- int ret;
- struct usbh_urb *urb = &pl2303_class->bulkin_urb;
-
- usbh_bulk_urb_fill(urb, pl2303_class->hport, pl2303_class->bulkin, buffer, buflen, timeout, NULL, NULL);
- ret = usbh_submit_urb(urb);
- if (ret == 0) {
- ret = urb->actual_length;
- }
- return ret;
-}
-
-int usbh_pl2303_bulk_out_transfer(struct usbh_pl2303 *pl2303_class, uint8_t *buffer, uint32_t buflen, uint32_t timeout)
-{
- int ret;
- struct usbh_urb *urb = &pl2303_class->bulkout_urb;
-
- usbh_bulk_urb_fill(urb, pl2303_class->hport, pl2303_class->bulkout, buffer, buflen, timeout, NULL, NULL);
- ret = usbh_submit_urb(urb);
- if (ret == 0) {
- ret = urb->actual_length;
- }
- return ret;
-}
-
-__WEAK void usbh_pl2303_run(struct usbh_pl2303 *pl2303_class)
-{
- (void)pl2303_class;
-}
-
-__WEAK void usbh_pl2303_stop(struct usbh_pl2303 *pl2303_class)
-{
- (void)pl2303_class;
-}
-
-static const uint16_t pl2303_id_table[][2] = {
- { 0x067B, 0x2303 }, // PL2303 Serial (ATEN/IOGEAR UC232A)
- { 0x067B, 0x23A3 }, // PL2303HXN Serial, type GC
- { 0x067B, 0x23B3 }, // PL2303HXN Serial, type GB
- { 0x067B, 0x23C3 }, // PL2303HXN Serial, type GT
- { 0x067B, 0x23D3 }, // PL2303HXN Serial, type GL
- { 0x067B, 0x23E3 }, // PL2303HXN Serial, type GE
- { 0x067B, 0x23F3 }, // PL2303HXN Serial, type GS
- { 0, 0 },
-};
-
-const struct usbh_class_driver pl2303_class_driver = {
- .driver_name = "pl2303",
- .connect = usbh_pl2303_connect,
- .disconnect = usbh_pl2303_disconnect
-};
-
-CLASS_INFO_DEFINE const struct usbh_class_info pl2303_class_info = {
- .match_flags = USB_CLASS_MATCH_VID_PID | USB_CLASS_MATCH_INTF_CLASS,
- .bInterfaceClass = 0xff,
- .bInterfaceSubClass = 0x00,
- .bInterfaceProtocol = 0x00,
- .id_table = pl2303_id_table,
- .class_driver = &pl2303_class_driver
-}; \ No newline at end of file
diff --git a/class/vendor/serial/usbh_pl2303.h b/class/vendor/serial/usbh_pl2303.h
deleted file mode 100644
index 2b3d05f9..00000000
--- a/class/vendor/serial/usbh_pl2303.h
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (c) 2024, sakumisu
- *
- * SPDX-License-Identifier: Apache-2.0
- */
-#ifndef USBH_PL2303_H
-#define USBH_PL2303_H
-
-#include "usb_cdc.h"
-
-#define PL2303_SET_REQUEST 0x01
-#define PL2303_SET_REQUEST_PL2303HXN 0x80
-#define PL2303_SET_CRTSCTS 0x41
-#define PL2303_SET_CRTSCTS_PL2303X 0x61
-#define PL2303_SET_CRTSCTS_PL2303HXN 0xFA
-#define PL2303_CLEAR_CRTSCTS_PL2303HXN 0xFF
-#define PL2303_CRTSCTS_REG_PL2303HXN 0x0A
-#define PL2303_STATUS_REG_PL2303HX 0x8080
-
-/* Different PL2303 IC types */
-#define USBH_PL2303_TYPE_UNKNOWN 0
-#define USBH_PL2303_TYPE_PL2303 1
-#define USBH_PL2303_TYPE_PL2303HX 2
-#define USBH_PL2303_TYPE_PL2303HXD 3
-#define USBH_PL2303_TYPE_PL2303HXN 4
-
-struct usbh_pl2303 {
- struct usbh_hubport *hport;
- struct usb_endpoint_descriptor *bulkin; /* Bulk IN endpoint */
- struct usb_endpoint_descriptor *bulkout; /* Bulk OUT endpoint */
-
- struct usbh_urb bulkout_urb;
- struct usbh_urb bulkin_urb;
-
- struct cdc_line_coding linecoding;
-
- uint8_t intf;
- uint8_t minor;
- uint8_t chiptype;
-
- void *user_data;
-};
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-int usbh_pl2303_set_line_coding(struct usbh_pl2303 *pl2303_class, struct cdc_line_coding *line_coding);
-int usbh_pl2303_get_line_coding(struct usbh_pl2303 *pl2303_class, struct cdc_line_coding *line_coding);
-int usbh_pl2303_set_line_state(struct usbh_pl2303 *pl2303_class, bool dtr, bool rts);
-
-int usbh_pl2303_bulk_in_transfer(struct usbh_pl2303 *pl2303_class, uint8_t *buffer, uint32_t buflen, uint32_t timeout);
-int usbh_pl2303_bulk_out_transfer(struct usbh_pl2303 *pl2303_class, uint8_t *buffer, uint32_t buflen, uint32_t timeout);
-
-void usbh_pl2303_run(struct usbh_pl2303 *pl2303_class);
-void usbh_pl2303_stop(struct usbh_pl2303 *pl2303_class);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* USBH_PL2303_H */
diff --git a/common/usb_util.h b/common/usb_util.h
index 90058193..521bf27f 100644
--- a/common/usb_util.h
+++ b/common/usb_util.h
@@ -209,6 +209,23 @@
19, 18, 17, 16, 15, 14, 13, 12, 11, 10, \
9, 8, 7, 6, 5, 4, 3, 2, 1, 0
+/*
+ * Divide positive or negative dividend by positive or negative divisor
+ * and round to closest integer. Result is undefined for negative
+ * divisors if the dividend variable type is unsigned and for negative
+ * dividends if the divisor variable type is unsigned.
+ */
+#define DIV_ROUND_CLOSEST(x, divisor) ( \
+ { \
+ typeof(x) __x = x; \
+ typeof(divisor) __d = divisor; \
+ (((typeof(x))-1) > 0 || \
+ ((typeof(divisor))-1) > 0 || \
+ (((__x) > 0) == ((__d) > 0))) ? \
+ (((__x) + ((__d) / 2)) / (__d)) : \
+ (((__x) - ((__d) / 2)) / (__d)); \
+ })
+
#define USB_MEM_ALIGNX __attribute__((aligned(CONFIG_USB_ALIGN_SIZE)))
#define USB_ALIGN_UP(size, align) (((size) + (align)-1) & ~((align)-1))
diff --git a/demo/usb_host.c b/demo/usb_host.c
index 89e78018..894856d0 100644
--- a/demo/usb_host.c
+++ b/demo/usb_host.c
@@ -4,20 +4,20 @@
* SPDX-License-Identifier: Apache-2.0
*/
#include "usbh_core.h"
-#include "usbh_cdc_acm.h"
+#include "usbh_serial.h"
#include "usbh_hid.h"
#include "usbh_msc.h"
#include "usbh_video.h"
#include "usbh_audio.h"
-#ifndef CONFIG_TEST_USBH_CDC_ACM
-#define CONFIG_TEST_USBH_CDC_ACM 1
+#ifndef CONFIG_TEST_USBH_SERIAL
+#define CONFIG_TEST_USBH_SERIAL 1
#endif
#ifndef TEST_USBH_CDC_SPEED
#define TEST_USBH_CDC_SPEED 0
#endif
#ifndef CONFIG_TEST_USBH_HID
-#define CONFIG_TEST_USBH_HID 1
+#define CONFIG_TEST_USBH_HID 0
#endif
#ifndef CONFIG_TEST_USBH_MSC
#define CONFIG_TEST_USBH_MSC 1
@@ -39,44 +39,62 @@
#error we have move those class implements into platform/none/usbh_lwip.c, and you should call tcpip_init(NULL, NULL) in your app
#endif
-#if CONFIG_TEST_USBH_CDC_ACM
-USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t cdc_buffer[4096];
+#if CONFIG_TEST_USBH_SERIAL
+#define SERIAL_TEST_LEN (2 * 1024)
+
+volatile uint32_t serial_tx_bytes = 0;
+volatile uint32_t serial_rx_bytes = 0;
+volatile bool serial_is_opened = false;
+
+USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t serial_tx_buffer[64];
+uint8_t serial_rx_data[SERIAL_TEST_LEN];
#if TEST_USBH_CDC_SPEED
#define TEST_LEN (16 * 1024)
#define TEST_COUNT (10240)
-USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t cdc_speed_buffer[TEST_LEN];
+USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t serial_speed_buffer[TEST_LEN];
#endif
-void usbh_cdc_acm_callback(void *arg, int nbytes)
+static void usbh_serial_thread(CONFIG_USB_OSAL_THREAD_SET_ARGV)
{
- //struct usbh_cdc_acm *cdc_acm_class = (struct usbh_cdc_acm *)arg;
+ int ret;
+ struct usbh_serial *serial;
- if (nbytes > 0) {
- for (size_t i = 0; i < nbytes; i++) {
- USB_LOG_RAW("0x%02x ", cdc_buffer[i]);
+ serial = usbh_serial_open("/dev/ttyACM0", USBH_SERIAL_O_RDWR | USBH_SERIAL_O_NONBLOCK);
+ if (serial == NULL) {
+ serial = usbh_serial_open("/dev/ttyUSB0", USBH_SERIAL_O_RDWR | USBH_SERIAL_O_NONBLOCK);
+ if (serial == NULL) {
+ USB_LOG_RAW("no serial device found\r\n");
+ goto delete;
}
- USB_LOG_RAW("nbytes:%d\r\n", (unsigned int)nbytes);
}
-}
-static void usbh_cdc_acm_thread(CONFIG_USB_OSAL_THREAD_SET_ARGV)
-{
- int ret;
- struct usbh_cdc_acm *cdc_acm_class = (struct usbh_cdc_acm *)CONFIG_USB_OSAL_THREAD_GET_ARGV;
+ struct usbh_serial_termios termios;
+
+ memset(&termios, 0, sizeof(termios));
+ termios.baudrate = 115200;
+ termios.stopbits = 0;
+ termios.parity = 0;
+ termios.databits = 8;
+ termios.rtscts = false;
+ termios.rx_timeout = 0;
+ ret = usbh_serial_control(serial, USBH_SERIAL_CMD_SET_ATTR, &termios);
+ if (ret < 0) {
+ USB_LOG_RAW("set serial attr error, ret:%d\r\n", ret);
+ goto delete_with_close;
+ }
/* test with only one buffer, if you have more cdc acm class, modify by yourself */
#if TEST_USBH_CDC_SPEED
const uint32_t test_len[] = { 512, 1 * 1024, 2 * 1024, 4 * 1024, 8 * 1024, 16 * 1024 };
- memset(cdc_speed_buffer, 0xAA, TEST_LEN);
+ memset(serial_speed_buffer, 0xAA, TEST_LEN);
for (uint8_t j = 0; j < 6; j++) {
uint32_t start_time = (uint32_t)xTaskGetTickCount();
for (uint32_t i = 0; i < TEST_COUNT; i++) {
- usbh_bulk_urb_fill(&cdc_acm_class->bulkout_urb, cdc_acm_class->hport, cdc_acm_class->bulkout, cdc_speed_buffer, test_len[j], 0XFFFFFFF, NULL, NULL);
- ret = usbh_submit_urb(&cdc_acm_class->bulkout_urb);
+ usbh_serial_write(serialize, serial_speed_buffer, test_len[j]);
if (ret < 0) {
USB_LOG_RAW("bulk out error,ret:%d\r\n", ret);
while (1) {
@@ -88,27 +106,62 @@ static void usbh_cdc_acm_thread(CONFIG_USB_OSAL_THREAD_SET_ARGV)
USB_LOG_RAW("per packet len:%d, out speed:%f MB/S\r\n", (unsigned int)test_len[j], (test_len[j] * TEST_COUNT / 1024 / 1024) * 1000 / ((float)time_ms));
}
#endif
- memset(cdc_buffer, 0x55, 4096);
+ memset(serial_tx_buffer, 0xA5, sizeof(serial_tx_buffer));
+ USB_LOG_RAW("start serial loopback test, len: %d\r\n", SERIAL_TEST_LEN);
- /* for common, we use timeout with 0xffffffff, this is just a test */
- usbh_bulk_urb_fill(&cdc_acm_class->bulkout_urb, cdc_acm_class->hport, cdc_acm_class->bulkout, cdc_buffer, sizeof(cdc_buffer), 3000, NULL, NULL);
- ret = usbh_submit_urb(&cdc_acm_class->bulkout_urb);
- if (ret < 0) {
- USB_LOG_RAW("bulk out error,ret:%d\r\n", ret);
- goto delete;
- } else {
- USB_LOG_RAW("send over:%d\r\n", (unsigned int)cdc_acm_class->bulkout_urb.actual_length);
+ serial_tx_bytes = 0;
+ while (1) {
+ /* for common, we use timeout with 0xffffffff, this is just a test */
+ ret = usbh_serial_write(serial, serial_tx_buffer, sizeof(serial_tx_buffer));
+ if (ret < 0) {
+ USB_LOG_RAW("serial write error, ret:%d\r\n", ret);
+ goto delete_with_close;
+ } else {
+ serial_tx_bytes += ret;
+ usb_osal_msleep(10); // 11.52 Byte/ms at 115200bps --> 64Byte/5.5ms
+
+ if (serial_tx_bytes == SERIAL_TEST_LEN) {
+ USB_LOG_RAW("send over\r\n");
+ break;
+ }
+ }
}
- /* we can change cdc_acm_class->bulkin->wMaxPacketSize with 4096 for testing zlp, default is ep mps */
- usbh_bulk_urb_fill(&cdc_acm_class->bulkin_urb, cdc_acm_class->hport, cdc_acm_class->bulkin, cdc_buffer, cdc_acm_class->bulkin->wMaxPacketSize, 0xffffffff, usbh_cdc_acm_callback, cdc_acm_class);
- ret = usbh_submit_urb(&cdc_acm_class->bulkin_urb);
- if (ret < 0) {
- USB_LOG_RAW("bulk in error,ret:%d\r\n", ret);
- goto delete;
- } else {
+ volatile uint32_t wait_timeout = 0;
+ serial_rx_bytes = 0;
+ while (1) {
+ ret = usbh_serial_read(serial, &serial_rx_data[serial_rx_bytes], SERIAL_TEST_LEN);
+ if (ret < 0) {
+ USB_LOG_RAW("serial read error, ret:%d\r\n", ret);
+ goto delete_with_close;
+ } else {
+ serial_rx_bytes += ret;
+
+ if (serial_rx_bytes == SERIAL_TEST_LEN) {
+ USB_LOG_RAW("receive over\r\n");
+ for (uint32_t i = 0; i < SERIAL_TEST_LEN; i++) {
+ if (serial_rx_data[i] != 0xa5) {
+ USB_LOG_RAW("serial loopback data error at index %d, data: 0x%02x\r\n", (unsigned int)i, serial_rx_data[i]);
+ goto delete_with_close;
+ }
+ }
+ USB_LOG_RAW("serial loopback test success\r\n");
+ break;
+ }
+ }
+ wait_timeout++;
+
+ if (wait_timeout > 500) { // 5s
+ USB_LOG_RAW("serial read timeout\r\n");
+ goto delete_with_close;
+ }
+
+ usb_osal_msleep(10);
}
+
// clang-format off
+delete_with_close:
+ usbh_serial_close(serial);
delete:
usb_osal_thread_delete(NULL);
// clang-format on
@@ -140,7 +193,6 @@ static void usbh_hid_thread(CONFIG_USB_OSAL_THREAD_SET_ARGV)
{
int ret;
struct usbh_hid *hid_class = (struct usbh_hid *)CONFIG_USB_OSAL_THREAD_GET_ARGV;
- ;
/* test with only one buffer, if you have more hid class, modify by yourself */
@@ -164,8 +216,8 @@ delete:
#if TEST_USBH_MSC_FATFS_SPEED
#define WRITE_SIZE_MB (128UL)
-#define WRITE_SIZE (1024UL * 1024UL * WRITE_SIZE_MB)
-#define BUF_SIZE (1024UL * 128UL)
+#define WRITE_SIZE (1024UL * 1024UL * WRITE_SIZE_MB)
+#define BUF_SIZE (1024UL * 128UL)
USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t read_write_buffer[BUF_SIZE];
#else
USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t read_write_buffer[25 * 100];
@@ -234,7 +286,7 @@ int usb_msc_fatfs_test()
uint32_t write_size = WRITE_SIZE;
uint32_t start_time = (uint32_t)xTaskGetTickCount();
while (write_size > 0) {
- res_sd = f_write(&fnew, read_write_buffer, BUF_SIZE, (UINT*)&fnum);
+ res_sd = f_write(&fnew, read_write_buffer, BUF_SIZE, (UINT *)&fnum);
if (res_sd != FR_OK) {
USB_LOG_RAW("Write file failed, cause: %s\n", res_sd);
goto unmount;
@@ -260,7 +312,7 @@ int usb_msc_fatfs_test()
uint32_t write_size = WRITE_SIZE;
uint32_t start_time = (uint32_t)xTaskGetTickCount();
while (write_size > 0) {
- res_sd = f_read(&fnew, read_write_buffer, BUF_SIZE, (UINT*)&fnum);
+ res_sd = f_read(&fnew, read_write_buffer, BUF_SIZE, (UINT *)&fnum);
if (res_sd != FR_OK) {
USB_LOG_RAW("Read file failed, cause: %s\n", res_sd);
goto unmount;
@@ -326,14 +378,19 @@ delete:
}
#endif
-#if CONFIG_TEST_USBH_CDC_ACM
-void usbh_cdc_acm_run(struct usbh_cdc_acm *cdc_acm_class)
+#if CONFIG_TEST_USBH_SERIAL
+void usbh_serial_run(struct usbh_serial *serial)
{
- usb_osal_thread_create("usbh_cdc", 2048, CONFIG_USBHOST_PSC_PRIO + 1, usbh_cdc_acm_thread, cdc_acm_class);
+ if (serial_is_opened) {
+ return;
+ }
+ serial_is_opened = true;
+ usb_osal_thread_create("usbh_serial", 2048, CONFIG_USBHOST_PSC_PRIO + 1, usbh_serial_thread, serial);
}
-void usbh_cdc_acm_stop(struct usbh_cdc_acm *cdc_acm_class)
+void usbh_serial_stop(struct usbh_serial *serial)
{
+ serial_is_opened = false;
}
#endif
diff --git a/docs/source/api/api_host.rst b/docs/source/api/api_host.rst
index 28287a0b..51b1c502 100644
--- a/docs/source/api/api_host.rst
+++ b/docs/source/api/api_host.rst
@@ -154,14 +154,115 @@ lsusb
int lsusb(int argc, char **argv);
-CDC ACM
+SERIAL
-----------------
+usbh_serial_open
+""""""""""""""""""""""""""""""""""""
+
+``usbh_serial_open`` 根据路径打开一个串口设备。
+
+.. code-block:: C
+
+ struct usbh_serial *usbh_serial_open(const char *devname, uint32_t open_flags);
+
+- **devname** 串口路径
+- **open_flags** 打开标志,参考 `USBH_SERIAL_OFLAG_*` 定义
+- **return** serial 结构体句柄
+
+usbh_serial_close
+""""""""""""""""""""""""""""""""""""
+
+``usbh_serial_close`` 关闭串口设备。
+
+.. code-block:: C
+
+ void usbh_serial_close(struct usbh_serial *serial);
+
+- **serial** serial 结构体句柄
+
+usbh_serial_control
+""""""""""""""""""""""""""""""""""""
+
+``usbh_serial_control`` 对串口进行配置。
+
+.. code-block:: C
+
+ int usbh_serial_control(struct usbh_serial *serial, int cmd, void *arg);
+
+- **serial** serial 结构体句柄
+- **cmd** 控制命令,参考 `USBH_SERIAL_CMD_*` 定义
+- **arg** 控制参数指针
+- **return** 0 表示正常其他表示错误
+
+usbh_serial_write
+""""""""""""""""""""""""""""""""""""
+
+``usbh_serial_write`` 向串口写数据。 **串口设备如果是 USB2TTL 类型,必须按照波特率发送,否则会丢包**
+
+.. code-block:: C
+
+ int usbh_serial_write(struct usbh_serial *serial, const void *buffer, uint32_t buflen);
+
+- **serial** serial 结构体句柄
+- **buffer** 数据缓冲区指针
+- **buflen** 要写入的数据长度,如果是 USB2TTL 设备,一次最高 wMaxPacketSize
+- **return** 实际写入的数据长度或者错误码
+
+.. note:: 有无设置波特率都可以使用该 API,当未设置波特率时,长度无限制,如果设置了波特率则为 wMaxPacketSize。
+
+usbh_serial_read
+""""""""""""""""""""""""""""""""""""
+
+``usbh_serial_read`` 从串口读数据。 **如果没有设置波特率,不允许使用该 API**。
+
+.. code-block:: C
+
+ int usbh_serial_read(struct usbh_serial *serial, void *buffer, uint32_t buflen);
+
+- **serial** serial 结构体句柄
+- **buffer** 数据缓冲区指针
+- **buflen** 要读取的最大数据长度
+- **return** 实际读取的数据长度或者错误码
+
+usbh_serial_cdc_write_async
+""""""""""""""""""""""""""""""""""""
+
+``usbh_serial_cdc_write_async`` 异步从串口读数据。 **如果设置了波特率,不允许使用该 API**。
+
+.. code-block:: C
+
+ int usbh_serial_cdc_write_async(struct usbh_serial *serial, uint8_t *buffer, uint32_t buflen, usbh_complete_callback_t complete, void *arg);
+
+- **serial** serial 结构体句柄
+- **buffer** 数据缓冲区指针
+- **buflen** 要发送的数据长度
+- **complete** 读数据完成回调函数
+- **arg** 回调函数参数
+- **return** 0 表示正常其他表示错误
+
+usbh_serial_cdc_read_async
+""""""""""""""""""""""""""""""""""""
+
+``usbh_serial_cdc_read_async`` 异步从串口读数据。 **如果设置了波特率,不允许使用该 API**。
+
+.. code-block:: C
+
+ int usbh_serial_cdc_read_async(struct usbh_serial *serial, uint8_t *buffer, uint32_t buflen, usbh_complete_callback_t complete, void *arg);
+
+- **serial** serial 结构体句柄
+- **buffer** 数据缓冲区指针
+- **buflen** 要读取的最大数据长度,一次最高 16K。并且需要是 wMaxPacketSize 的整数倍
+- **complete** 读数据完成回调函数
+- **arg** 回调函数参数
+- **return** 0 表示正常其他表示错误
+
+
HID
-----------------
MSC
-----------------
-RNDIS
+NETWORK
----------------- \ No newline at end of file
diff --git a/docs/source/demo/usbh_serial.rst b/docs/source/demo/usbh_serial.rst
index 58355cfe..c3c99255 100644
--- a/docs/source/demo/usbh_serial.rst
+++ b/docs/source/demo/usbh_serial.rst
@@ -1,4 +1,96 @@
usbh_serial
===============
-当前仅支持 rt-thread device 框架,包括 cdc acm, ftdi, cp210x, ch34x, pl2303, 具体使用方式参考 rt-thread device api 即可。 \ No newline at end of file
+Serial 框架当前支持 cdc acm, ftdi, cp210x, ch34x, pl2303,gsm 驱动。当前支持两种使用方式,
+一种是使用源生 CherryUSB usbhost serial API 进行操作,另一种是基于平台封装的 API 操作,比如 rt-thread device API。,nuttx posix API。
+
+下面演示的是使用 CherryUSB usbhost serial API 进行串口回环测试,并且使用阻塞发送,异步读取的方式:
+
+.. code-block:: C
+
+ struct usbh_serial *serial;
+
+ serial = usbh_serial_open("/dev/ttyACM0", USBH_SERIAL_O_RDWR | USBH_SERIAL_O_NONBLOCK);
+ if (serial == NULL) {
+ serial = usbh_serial_open("/dev/ttyUSB0", USBH_SERIAL_O_RDWR | USBH_SERIAL_O_NONBLOCK);
+ if (serial == NULL) {
+ USB_LOG_RAW("no serial device found\r\n");
+ goto delete;
+ }
+ }
+
+ struct usbh_serial_termios termios;
+
+ memset(&termios, 0, sizeof(termios));
+ termios.baudrate = 115200;
+ termios.stopbits = 0;
+ termios.parity = 0;
+ termios.databits = 8;
+ termios.rtscts = false;
+ termios.rx_timeout = 0;
+ ret = usbh_serial_control(serial, USBH_SERIAL_CMD_SET_ATTR, &termios);
+ if (ret < 0) {
+ USB_LOG_RAW("set serial attr error, ret:%d\r\n", ret);
+ goto delete_with_close;
+ }
+
+ serial_tx_bytes = 0;
+ while (1) {
+ /* for common, we use timeout with 0xffffffff, this is just a test */
+ ret = usbh_serial_write(serial, serial_tx_buffer, sizeof(serial_tx_buffer));
+ if (ret < 0) {
+ USB_LOG_RAW("serial write error, ret:%d\r\n", ret);
+ goto delete_with_close;
+ } else {
+ serial_tx_bytes += ret;
+ usb_osal_msleep(10); // 11.52 Byte/ms at 115200bps --> 64Byte/5.5ms
+
+ if (serial_tx_bytes == SERIAL_TEST_LEN) {
+ USB_LOG_RAW("send over\r\n");
+ break;
+ }
+ }
+ }
+
+ volatile uint32_t wait_timeout = 0;
+ serial_rx_bytes = 0;
+ while (1) {
+ ret = usbh_serial_read(serial, &serial_rx_data[serial_rx_bytes], SERIAL_TEST_LEN);
+ if (ret < 0) {
+ USB_LOG_RAW("serial read error, ret:%d\r\n", ret);
+ goto delete_with_close;
+ } else {
+ serial_rx_bytes += ret;
+
+ if (serial_rx_bytes == SERIAL_TEST_LEN) {
+ USB_LOG_RAW("receive over\r\n");
+ for (uint32_t i = 0; i < SERIAL_TEST_LEN; i++) {
+ if (serial_rx_data[i] != 0xa5) {
+ USB_LOG_RAW("serial loopback data error at index %d, data: 0x%02x\r\n", (unsigned int)i, serial_rx_data[i]);
+ goto delete_with_close;
+ }
+ }
+ USB_LOG_RAW("serial loopback test success\r\n");
+ break;
+ }
+ }
+ wait_timeout++;
+
+ if (wait_timeout > 500) { // 5s
+ USB_LOG_RAW("serial read timeout\r\n");
+ goto delete_with_close;
+ }
+
+ usb_osal_msleep(10);
+ }
+
+ usbh_serial_close(serial);
+
+
+用户需要考虑以下三种场景:
+
+- USB2TTL 设备 + 启用了波特率,这种情况下需要使用 `usbh_serial_write` 和 `usbh_serial_read` 进行收发数据, **并且需要根据波特率控制发送频率,防止对端丢包**;
+
+- 纯 USB 设备 + 未启动波特率,这种情况下可以使用 `usbh_serial_cdc_write_async` 和 `usbh_serial_cdc_read_async` 进行异步收发数据,阻塞则用 `usbh_serial_write` 并且不需要控制发送频率。不可以使用 `usbh_serial_read`。
+
+- 纯 USB 设备 + 启动波特率,同 1,但是速率打折扣。不可以使用 `usbh_serial_cdc_write_async` 和 `usbh_serial_cdc_read_async`。如果是 GSM 设备需要使用第一种。 \ No newline at end of file
diff --git a/osal/idf/usb_config.h b/osal/idf/usb_config.h
index 3eca2e79..c443c12e 100644
--- a/osal/idf/usb_config.h
+++ b/osal/idf/usb_config.h
@@ -28,7 +28,7 @@
// #define CONFIG_USB_DCACHE_ENABLE
-/* attribute data into no cache ram
+/* attribute data into no cache ram
* DRAM_DMA_ALIGNED_ATTR was introduced in IDF 5.3. If not defined, it falls back to DMA_ATTR
*/
#ifndef DRAM_DMA_ALIGNED_ATTR
@@ -158,7 +158,7 @@
#define CONFIG_USBHOST_MAX_INTF_ALTSETTINGS 2
#define CONFIG_USBHOST_MAX_ENDPOINTS 4
-#define CONFIG_USBHOST_MAX_CDC_ACM_CLASS 4
+#define CONFIG_USBHOST_MAX_SERIAL_CLASS 4
#define CONFIG_USBHOST_MAX_HID_CLASS 4
#define CONFIG_USBHOST_MAX_MSC_CLASS 2
#define CONFIG_USBHOST_MAX_AUDIO_CLASS 1
@@ -193,6 +193,10 @@
#define CONFIG_USBHOST_MSC_TIMEOUT 5000
#endif
+#ifndef CONFIG_USBHOST_SERIAL_RX_SIZE
+#define CONFIG_USBHOST_SERIAL_RX_SIZE 2048
+#endif
+
/* This parameter affects usb performance, and depends on (TCP_WND)tcp eceive windows size,
* you can change to 2K ~ 16K and must be larger than TCP RX windows size in order to avoid being overflow.
*/
diff --git a/platform/rtthread/usb_msh.c b/platform/rtthread/usb_msh.c
index fc5cdaee..8dd61164 100644
--- a/platform/rtthread/usb_msh.c
+++ b/platform/rtthread/usb_msh.c
@@ -44,4 +44,10 @@ int usbh_deinit(int argc, char **argv)
MSH_CMD_EXPORT(usbh_init, init usb host);
MSH_CMD_EXPORT(usbh_deinit, deinit usb host);
MSH_CMD_EXPORT(lsusb, ls usb devices);
+
+#ifdef CONFIG_USBHOST_SERIAL
+#include "usbh_serial.h"
+MSH_CMD_EXPORT(usbh_serial, usbh_serial test);
+#endif
+
#endif
diff --git a/platform/rtthread/usbh_rtserial.c b/platform/rtthread/usbh_rtserial.c
new file mode 100644
index 00000000..f1e25503
--- /dev/null
+++ b/platform/rtthread/usbh_rtserial.c
@@ -0,0 +1,209 @@
+/*
+ * Copyright (c) 2025, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#include <rtthread.h>
+#include <rtdevice.h>
+
+#include "usbh_core.h"
+#include "usbh_serial.h"
+
+static rt_err_t rt_usbh_serial_open(struct rt_device *dev, rt_uint16_t oflag)
+{
+ struct usbh_serial *serial;
+
+ RT_ASSERT(dev != RT_NULL && dev->user_data != RT_NULL);
+
+ serial = (struct usbh_serial *)dev->user_data;
+
+ serial = usbh_serial_open(serial->hport->config.intf[serial->intf].devname, USBH_SERIAL_O_RDWR | USBH_SERIAL_O_NONBLOCK);
+ if (serial == RT_NULL) {
+ USB_LOG_ERR("serial open failed\n");
+ return -RT_ERROR;
+ }
+
+ struct usbh_serial_termios termios;
+
+ memset(&termios, 0, sizeof(termios));
+ termios.baudrate = 115200;
+ termios.stopbits = 0;
+ termios.parity = 0;
+ termios.databits = 8;
+ termios.rtscts = false;
+ termios.rx_timeout = 0;
+
+ usbh_serial_control(serial, USBH_SERIAL_CMD_SET_ATTR, &termios);
+
+ return RT_EOK;
+}
+
+static rt_err_t rt_usbh_serial_close(struct rt_device *dev)
+{
+ struct usbh_serial *serial;
+
+ RT_ASSERT(dev != RT_NULL && dev->user_data != RT_NULL);
+
+ serial = (struct usbh_serial *)dev->user_data;
+
+ usbh_serial_close(serial);
+
+ return RT_EOK;
+}
+
+static rt_ssize_t rt_usbh_serial_read(struct rt_device *dev,
+ rt_off_t pos,
+ void *buffer,
+ rt_size_t size)
+{
+ struct usbh_serial *serial;
+
+ RT_ASSERT(dev != RT_NULL && dev->user_data != RT_NULL);
+
+ serial = (struct usbh_serial *)dev->user_data;
+
+ return usbh_serial_read(serial, buffer, size);
+}
+
+static rt_ssize_t rt_usbh_serial_write(struct rt_device *dev,
+ rt_off_t pos,
+ const void *buffer,
+ rt_size_t size)
+{
+ struct usbh_serial *serial;
+ int ret = 0;
+ rt_uint8_t *align_buf;
+
+ RT_ASSERT(dev != RT_NULL && dev->user_data != RT_NULL);
+
+ serial = (struct usbh_serial *)dev->user_data;
+
+ align_buf = (rt_uint8_t *)buffer;
+
+ if ((uint32_t)buffer & (CONFIG_USB_ALIGN_SIZE - 1)) {
+ align_buf = rt_malloc_align(USB_ALIGN_UP(size, CONFIG_USB_ALIGN_SIZE), CONFIG_USB_ALIGN_SIZE);
+ if (!align_buf) {
+ USB_LOG_ERR("serial get align buf failed\n");
+ return 0;
+ }
+
+ usb_memcpy(align_buf, buffer, size);
+ }
+
+ ret = usbh_serial_write(serial, align_buf, size);
+
+ if ((uint32_t)buffer & (CONFIG_USB_ALIGN_SIZE - 1)) {
+ rt_free_align(align_buf);
+ }
+
+ return ret;
+}
+
+static rt_err_t rt_usbh_serial_control(struct rt_device *dev,
+ int cmd,
+ void *args)
+{
+ struct usbh_serial *serial;
+ struct serial_configure *config;
+ int ret = -RT_EINVAL;
+
+ RT_ASSERT(dev != RT_NULL && dev->user_data != RT_NULL);
+
+ serial = (struct usbh_serial *)dev->user_data;
+
+ switch (cmd) {
+ case RT_DEVICE_CTRL_CONFIG: {
+ config = (struct serial_configure *)args;
+
+ struct usbh_serial_termios termios;
+
+ memset(&termios, 0, sizeof(termios));
+ termios.baudrate = config->baud_rate;
+ termios.stopbits = 0;
+ termios.parity = config->parity;
+ termios.databits = config->data_bits;
+ termios.rtscts = false;
+ termios.rx_timeout = 0;
+
+ usbh_serial_control(serial, USBH_SERIAL_CMD_SET_ATTR, &termios);
+ } break;
+
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+#ifdef RT_USING_DEVICE_OPS
+const static struct rt_device_ops usbh_serial_ops = {
+ NULL,
+ rt_usbh_serial_open,
+ rt_usbh_serial_close,
+ rt_usbh_serial_read,
+ rt_usbh_serial_write,
+ rt_usbh_serial_control
+};
+#endif
+
+rt_err_t usbh_serial_register(struct usbh_serial *serial)
+{
+ rt_err_t ret;
+ struct rt_device *device;
+ RT_ASSERT(serial != RT_NULL);
+
+ device = rt_malloc(sizeof(struct rt_device));
+ if (device == RT_NULL) {
+ USB_LOG_ERR("serial device malloc failed\n");
+ return -RT_ENOMEM;
+ }
+ memset(device, 0, sizeof(struct rt_device));
+
+ device->type = RT_Device_Class_Char;
+ device->rx_indicate = RT_NULL;
+ device->tx_complete = RT_NULL;
+
+#ifdef RT_USING_DEVICE_OPS
+ device->ops = &usbh_serial_ops;
+#else
+ device->init = NULL;
+ device->open = rt_usbh_serial_open;
+ device->close = rt_usbh_serial_close;
+ device->read = rt_usbh_serial_read;
+ device->write = rt_usbh_serial_write;
+ device->control = rt_usbh_serial_control;
+#endif
+ device->user_data = serial;
+ serial->user_data = device;
+
+ /* register a character device */
+ ret = rt_device_register(device, serial->hport->config.intf[serial->intf].devname, RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX | RT_DEVICE_FLAG_REMOVABLE);
+
+#ifdef RT_USING_POSIX_DEVIO
+ /* set fops */
+ device->fops = &usbh_serial_fops;
+#endif
+ return ret;
+}
+
+void usbh_serial_unregister(struct usbh_serial *serial)
+{
+ struct rt_device *device;
+
+ RT_ASSERT(serial != NULL && serial->user_data != NULL);
+
+ device = (struct rt_device *)serial->user_data;
+
+ rt_device_unregister(device);
+ rt_free(device);
+}
+
+void usbh_serial_run(struct usbh_serial *serial)
+{
+ usbh_serial_register(serial);
+}
+
+void usbh_serial_stop(struct usbh_serial *serial)
+{
+ usbh_serial_unregister(serial);
+} \ No newline at end of file
diff --git a/platform/rtthread/usbh_serial.c b/platform/rtthread/usbh_serial.c
deleted file mode 100644
index 912258e0..00000000
--- a/platform/rtthread/usbh_serial.c
+++ /dev/null
@@ -1,899 +0,0 @@
-/*
- * Copyright (c) 2025, sakumisu
- *
- * SPDX-License-Identifier: Apache-2.0
- */
-#include <rtthread.h>
-#include <rtdevice.h>
-
-#include "usbh_core.h"
-#include "usbh_cdc_acm.h"
-#include "usbh_ftdi.h"
-#include "usbh_cp210x.h"
-#include "usbh_ch34x.h"
-#include "usbh_pl2303.h"
-
-#define DEV_FORMAT_VENDOR "ttyUSB%d"
-#define DEV_FORMAT_CDC_ACM "ttyACM%d"
-
-#define USBH_RX_MAX_SIZE 2048
-
-#ifndef CONFIG_USBHOST_MAX_VENDOR_SERIAL_CLASS
-#define CONFIG_USBHOST_MAX_VENDOR_SERIAL_CLASS (4)
-#endif
-
-#ifndef CONFIG_USBHOST_SERIAL_RX_BUFSIZE
-#define CONFIG_USBHOST_SERIAL_RX_BUFSIZE (USBH_RX_MAX_SIZE * 2)
-#endif
-
-enum usbh_serial_type {
- USBH_SERIAL_TYPE_CDC_ACM = 0,
- USBH_SERIAL_TYPE_FTDI,
- USBH_SERIAL_TYPE_CP210X,
- USBH_SERIAL_TYPE_CH34X,
- USBH_SERIAL_TYPE_PL2303,
-};
-
-struct usbh_serial {
- struct rt_device parent;
- enum usbh_serial_type type;
- uint8_t minor;
- char name[CONFIG_USBHOST_DEV_NAMELEN];
- struct rt_ringbuffer rx_rb;
- rt_uint8_t rx_rb_buffer[CONFIG_USBHOST_SERIAL_RX_BUFSIZE];
-};
-
-static uint32_t g_devinuse_vendor = 0;
-static uint32_t g_devinuse_cdc_acm = 0;
-
-static USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_usbh_serial_vendor_rx_buf[CONFIG_USBHOST_MAX_VENDOR_SERIAL_CLASS][USB_ALIGN_UP(USBH_RX_MAX_SIZE, CONFIG_USB_ALIGN_SIZE)];
-static USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_usbh_serial_cdc_acm_rx_buf[CONFIG_USBHOST_MAX_CDC_ACM_CLASS][USB_ALIGN_UP(USBH_RX_MAX_SIZE, CONFIG_USB_ALIGN_SIZE)];
-
-static struct usbh_serial *usbh_serial_alloc(uint8_t type)
-{
- uint8_t devno;
- struct usbh_serial *serial;
-
- for (devno = 0; devno < CONFIG_USBHOST_MAX_VENDOR_SERIAL_CLASS; devno++) {
- if ((g_devinuse_vendor & (1U << devno)) == 0) {
- g_devinuse_vendor |= (1U << devno);
-
- serial = rt_malloc(sizeof(struct usbh_serial));
- memset(serial, 0, sizeof(struct usbh_serial));
- serial->type = type;
- serial->minor = devno;
- snprintf(serial->name, CONFIG_USBHOST_DEV_NAMELEN, DEV_FORMAT_VENDOR, serial->minor);
- return serial;
- }
- }
- return NULL;
-}
-
-static void usbh_serial_free(struct usbh_serial *serial)
-{
- uint8_t devno = serial->minor;
-
- if (devno < 32) {
- g_devinuse_vendor &= ~(1U << devno);
- }
- memset(serial, 0, sizeof(struct usbh_serial));
- rt_free(serial);
-}
-
-static struct usbh_serial *usbh_serial_cdc_acm_alloc(uint8_t type)
-{
- uint8_t devno;
- struct usbh_serial *serial;
-
- for (devno = 0; devno < CONFIG_USBHOST_MAX_CDC_ACM_CLASS; devno++) {
- if ((g_devinuse_cdc_acm & (1U << devno)) == 0) {
- g_devinuse_cdc_acm |= (1U << devno);
-
- serial = rt_malloc(sizeof(struct usbh_serial));
- memset(serial, 0, sizeof(struct usbh_serial));
- serial->type = type;
- serial->minor = devno;
- snprintf(serial->name, CONFIG_USBHOST_DEV_NAMELEN, DEV_FORMAT_CDC_ACM, serial->minor);
- return serial;
- }
- }
- return NULL;
-}
-
-static void usbh_serial_cdc_acm_free(struct usbh_serial *serial)
-{
- uint8_t devno = serial->minor;
-
- if (devno < 32) {
- g_devinuse_cdc_acm &= ~(1U << devno);
- }
- memset(serial, 0, sizeof(struct usbh_serial));
- rt_free(serial);
-}
-
-static rt_err_t usbh_serial_open(struct rt_device *dev, rt_uint16_t oflag)
-{
- struct usbh_serial *serial;
-
- RT_ASSERT(dev != RT_NULL);
-
- serial = (struct usbh_serial *)dev;
-
- switch (serial->type) {
- case USBH_SERIAL_TYPE_CDC_ACM:
- break;
- case USBH_SERIAL_TYPE_FTDI:
- break;
- case USBH_SERIAL_TYPE_CP210X:
- break;
- case USBH_SERIAL_TYPE_CH34X:
- break;
- case USBH_SERIAL_TYPE_PL2303:
- break;
-
- default:
- break;
- }
-
- return RT_EOK;
-}
-
-static rt_err_t usbh_serial_close(struct rt_device *dev)
-{
- struct usbh_serial *serial;
-
- RT_ASSERT(dev != RT_NULL);
-
- serial = (struct usbh_serial *)dev;
-
- switch (serial->type) {
- case USBH_SERIAL_TYPE_CDC_ACM:
- break;
- case USBH_SERIAL_TYPE_FTDI:
- break;
- case USBH_SERIAL_TYPE_CP210X:
- break;
- case USBH_SERIAL_TYPE_CH34X:
- break;
- case USBH_SERIAL_TYPE_PL2303:
- break;
-
- default:
- break;
- }
-
- return RT_EOK;
-}
-
-static rt_ssize_t usbh_serial_read(struct rt_device *dev,
- rt_off_t pos,
- void *buffer,
- rt_size_t size)
-{
- struct usbh_serial *serial;
-
- RT_ASSERT(dev != RT_NULL);
-
- serial = (struct usbh_serial *)dev;
-
- return rt_ringbuffer_get(&serial->rx_rb, (rt_uint8_t *)buffer, size);
-}
-
-static rt_ssize_t usbh_serial_write(struct rt_device *dev,
- rt_off_t pos,
- const void *buffer,
- rt_size_t size)
-{
- struct usbh_serial *serial;
- int ret = 0;
- rt_uint8_t *align_buf;
-
- RT_ASSERT(dev != RT_NULL);
-
- serial = (struct usbh_serial *)dev;
-
- align_buf = (rt_uint8_t *)buffer;
-
- if ((uint32_t)buffer & (CONFIG_USB_ALIGN_SIZE - 1)) {
- align_buf = rt_malloc_align(USB_ALIGN_UP(size, CONFIG_USB_ALIGN_SIZE), CONFIG_USB_ALIGN_SIZE);
- if (!align_buf) {
- USB_LOG_ERR("serial get align buf failed\n");
- return 0;
- }
-
- usb_memcpy(align_buf, buffer, size);
- }
-
- switch (serial->type) {
-#if defined(PKG_CHERRYUSB_HOST_CDC_ACM) || defined(RT_CHERRYUSB_HOST_CDC_ACM)
- case USBH_SERIAL_TYPE_CDC_ACM:
- ret = usbh_cdc_acm_bulk_out_transfer((struct usbh_cdc_acm *)dev->user_data, (uint8_t *)align_buf, size, RT_WAITING_FOREVER);
- if (ret < 0) {
- USB_LOG_ERR("usbh_cdc_acm_bulk_out_transfer failed: %d\n", ret);
- ret = 0;
- }
- break;
-#endif
-#if defined(PKG_CHERRYUSB_HOST_FTDI) || defined(RT_CHERRYUSB_HOST_FTDI)
- case USBH_SERIAL_TYPE_FTDI:
- ret = usbh_ftdi_bulk_out_transfer((struct usbh_ftdi *)dev->user_data, (uint8_t *)align_buf, size, RT_WAITING_FOREVER);
- if (ret < 0) {
- USB_LOG_ERR("usbh_ftdi_bulk_out_transfer failed: %d\n", ret);
- ret = 0;
- }
- break;
-#endif
-#if defined(PKG_CHERRYUSB_HOST_CH34X) || defined(RT_CHERRYUSB_HOST_CH34X)
- case USBH_SERIAL_TYPE_CH34X:
- ret = usbh_ch34x_bulk_out_transfer((struct usbh_ch34x *)dev->user_data, (uint8_t *)align_buf, size, RT_WAITING_FOREVER);
- if (ret < 0) {
- USB_LOG_ERR("usbh_ch34x_bulk_out_transfer failed: %d\n", ret);
- ret = 0;
- }
- break;
-#endif
-#if defined(PKG_CHERRYUSB_HOST_PL2303) || defined(RT_CHERRYUSB_HOST_PL2303)
- case USBH_SERIAL_TYPE_PL2303:
- ret = usbh_pl2303_bulk_out_transfer((struct usbh_pl2303 *)dev->user_data, (uint8_t *)align_buf, size, RT_WAITING_FOREVER);
- if (ret < 0) {
- USB_LOG_ERR("usbh_pl2303_bulk_out_transfer failed: %d\n", ret);
- ret = 0;
- }
- break;
-#endif
- default:
- break;
- }
-
- if ((uint32_t)buffer & (CONFIG_USB_ALIGN_SIZE - 1)) {
- rt_free_align(align_buf);
- }
-
- return ret;
-}
-
-static rt_err_t usbh_serial_control(struct rt_device *dev,
- int cmd,
- void *args)
-{
- struct usbh_serial *serial;
- struct serial_configure *config;
- struct cdc_line_coding line_coding;
- int ret = -RT_EINVAL;
-
- RT_ASSERT(dev != RT_NULL);
-
- serial = (struct usbh_serial *)dev;
-
- switch (serial->type) {
-#if defined(PKG_CHERRYUSB_HOST_CDC_ACM) || defined(RT_CHERRYUSB_HOST_CDC_ACM)
- case USBH_SERIAL_TYPE_CDC_ACM:
- if (cmd == RT_DEVICE_CTRL_CONFIG) {
- struct usbh_cdc_acm *cdc_acm_class;
- cdc_acm_class = (struct usbh_cdc_acm *)dev->user_data;
-
- config = (struct serial_configure *)args;
-
- line_coding.dwDTERate = config->baud_rate;
- line_coding.bDataBits = config->data_bits;
- line_coding.bCharFormat = 0; // STOP_BITS_1
- line_coding.bParityType = config->parity;
-
- usbh_cdc_acm_set_line_coding(cdc_acm_class, &line_coding);
- }
-
- ret = RT_EOK;
- break;
-#endif
-#if defined(PKG_CHERRYUSB_HOST_FTDI) || defined(RT_CHERRYUSB_HOST_FTDI)
- case USBH_SERIAL_TYPE_FTDI:
- if (cmd == RT_DEVICE_CTRL_CONFIG) {
- struct usbh_ftdi *ftdi_class;
- ftdi_class = (struct usbh_ftdi *)dev->user_data;
-
- config = (struct serial_configure *)args;
-
- line_coding.dwDTERate = config->baud_rate;
- line_coding.bDataBits = config->data_bits;
- line_coding.bCharFormat = 0; // STOP_BITS_1
- line_coding.bParityType = config->parity;
-
- usbh_ftdi_set_line_coding(ftdi_class, &line_coding);
- }
-
- ret = RT_EOK;
- break;
-#endif
-#if defined(PKG_CHERRYUSB_HOST_CP210X) || defined(RT_CHERRYUSB_HOST_CP210X)
- case USBH_SERIAL_TYPE_CP210X:
- if (cmd == RT_DEVICE_CTRL_CONFIG) {
- struct usbh_cp210x *cp210x_class;
- cp210x_class = (struct usbh_cp210x *)dev->user_data;
-
- config = (struct serial_configure *)args;
-
- line_coding.dwDTERate = config->baud_rate;
- line_coding.bDataBits = config->data_bits;
- line_coding.bCharFormat = 0; // STOP_BITS_1
- line_coding.bParityType = config->parity;
-
- usbh_cp210x_set_line_coding(cp210x_class, &line_coding);
- }
-
- ret = RT_EOK;
- break;
-#endif
-#if defined(PKG_CHERRYUSB_HOST_CH34X) || defined(RT_CHERRYUSB_HOST_CH34X)
- case USBH_SERIAL_TYPE_CH34X:
- if (cmd == RT_DEVICE_CTRL_CONFIG) {
- struct usbh_ch34x *ch34x_class;
- ch34x_class = (struct usbh_ch34x *)dev->user_data;
-
- config = (struct serial_configure *)args;
-
- line_coding.dwDTERate = config->baud_rate;
- line_coding.bDataBits = config->data_bits;
- line_coding.bCharFormat = 0; // STOP_BITS_1
- line_coding.bParityType = config->parity;
-
- usbh_ch34x_set_line_coding(ch34x_class, &line_coding);
- }
-
- ret = RT_EOK;
- break;
-#endif
-#if defined(PKG_CHERRYUSB_HOST_PL2303) || defined(RT_CHERRYUSB_HOST_PL2303)
- case USBH_SERIAL_TYPE_PL2303:
- if (cmd == RT_DEVICE_CTRL_CONFIG) {
- struct usbh_pl2303 *pl2303_class;
- pl2303_class = (struct usbh_pl2303 *)dev->user_data;
-
- config = (struct serial_configure *)args;
-
- line_coding.dwDTERate = config->baud_rate;
- line_coding.bDataBits = config->data_bits;
- line_coding.bCharFormat = 0; // STOP_BITS_1
- line_coding.bParityType = config->parity;
-
- usbh_pl2303_set_line_coding(pl2303_class, &line_coding);
- }
-
- ret = RT_EOK;
- break;
-#endif
- default:
- break;
- }
-
- return ret;
-}
-
-#ifdef RT_USING_DEVICE_OPS
-const static struct rt_device_ops usbh_serial_ops = {
- NULL,
- usbh_serial_open,
- usbh_serial_close,
- usbh_serial_read,
- usbh_serial_write,
- usbh_serial_control
-};
-#endif
-
-#ifdef RT_USING_POSIX_DEVIO
-#include <unistd.h>
-#include <fcntl.h>
-#include <poll.h>
-#include <sys/ioctl.h>
-#include <dfs_file.h>
-
-#ifdef RT_USING_POSIX_TERMIOS
-#include <termios.h>
-#endif
-
-static rt_err_t usbh_serial_fops_rx_ind(rt_device_t dev, rt_size_t size)
-{
- rt_wqueue_wakeup(&(dev->wait_queue), (void*)POLLIN);
-
- return RT_EOK;
-}
-
-/* fops for serial */
-static int usbh_serial_fops_open(struct dfs_file *fd)
-{
- rt_err_t ret = 0;
- rt_uint16_t flags = 0;
- rt_device_t device;
-
- device = (rt_device_t)fd->vnode->data;
- RT_ASSERT(device != RT_NULL);
-
- switch (fd->flags & O_ACCMODE)
- {
- case O_RDONLY:
- USB_LOG_DBG("fops open: O_RDONLY!");
- flags = RT_DEVICE_FLAG_RDONLY;
- break;
- case O_WRONLY:
- USB_LOG_DBG("fops open: O_WRONLY!");
- flags = RT_DEVICE_FLAG_WRONLY;
- break;
- case O_RDWR:
- USB_LOG_DBG("fops open: O_RDWR!");
- flags = RT_DEVICE_FLAG_RDWR;
- break;
- default:
- USB_LOG_ERR("fops open: unknown mode - %d!", fd->flags & O_ACCMODE);
- break;
- }
-
- if ((fd->flags & O_ACCMODE) != O_WRONLY)
- rt_device_set_rx_indicate(device, usbh_serial_fops_rx_ind);
- ret = rt_device_open(device, flags);
- if (ret == RT_EOK) return 0;
-
- return ret;
-}
-
-static int usbh_serial_fops_close(struct dfs_file *fd)
-{
- rt_device_t device;
-
- device = (rt_device_t)fd->vnode->data;
-
- rt_device_set_rx_indicate(device, RT_NULL);
- rt_device_close(device);
-
- return 0;
-}
-
-static int usbh_serial_fops_ioctl(struct dfs_file *fd, int cmd, void *args)
-{
- rt_device_t device;
- int flags = (int)(rt_base_t)args;
- int mask = O_NONBLOCK | O_APPEND;
-
- device = (rt_device_t)fd->vnode->data;
- switch (cmd)
- {
- case FIONREAD:
- break;
- case FIONWRITE:
- break;
- case F_SETFL:
- flags &= mask;
- fd->flags &= ~mask;
- fd->flags |= flags;
- break;
- }
-
- return rt_device_control(device, cmd, args);
-}
-
-static int usbh_serial_fops_read(struct dfs_file *fd, void *buf, size_t count)
-{
- int size = 0;
- rt_device_t device;
-
- device = (rt_device_t)fd->vnode->data;
-
- do
- {
- size = rt_device_read(device, -1, buf, count);
- if (size <= 0)
- {
- if (fd->flags & O_NONBLOCK)
- {
- size = -EAGAIN;
- break;
- }
-
- rt_wqueue_wait(&(device->wait_queue), 0, RT_WAITING_FOREVER);
- }
- }while (size <= 0);
-
- return size;
-}
-
-static int usbh_serial_fops_write(struct dfs_file *fd, const void *buf, size_t count)
-{
- rt_device_t device;
-
- device = (rt_device_t)fd->vnode->data;
- return rt_device_write(device, -1, buf, count);
-}
-
-static int usbh_serial_fops_poll(struct dfs_file *fd, struct rt_pollreq *req)
-{
- int mask = 0;
- int flags = 0;
- rt_device_t device;
- struct usbh_serial *serial;
-
- device = (rt_device_t)fd->vnode->data;
- RT_ASSERT(device != RT_NULL);
-
- serial = (struct usbh_serial *)device;
-
- /* only support POLLIN */
- flags = fd->flags & O_ACCMODE;
- if (flags == O_RDONLY || flags == O_RDWR)
- {
- rt_base_t level;
-
- rt_poll_add(&(device->wait_queue), req);
-
- level = rt_hw_interrupt_disable();
-
- if (rt_ringbuffer_data_len(&serial->rx_rb))
- mask |= POLLIN;
- rt_hw_interrupt_enable(level);
- }
- // mask|=POLLOUT;
- return mask;
-}
-
-const static struct dfs_file_ops usbh_serial_fops =
-{
- usbh_serial_fops_open,
- usbh_serial_fops_close,
- usbh_serial_fops_ioctl,
- usbh_serial_fops_read,
- usbh_serial_fops_write,
- RT_NULL, /* flush */
- RT_NULL, /* lseek */
- RT_NULL, /* getdents */
- usbh_serial_fops_poll,
-};
-#endif /* RT_USING_POSIX_DEVIO */
-
-rt_err_t usbh_serial_register(struct usbh_serial *serial,
- void *data)
-{
- rt_err_t ret;
- struct rt_device *device;
- RT_ASSERT(serial != RT_NULL);
-
- device = &(serial->parent);
-
- device->type = RT_Device_Class_Char;
- device->rx_indicate = RT_NULL;
- device->tx_complete = RT_NULL;
-
-#ifdef RT_USING_DEVICE_OPS
- device->ops = &usbh_serial_ops;
-#else
- device->init = NULL;
- device->open = usbh_serial_open;
- device->close = usbh_serial_close;
- device->read = usbh_serial_read;
- device->write = usbh_serial_write;
- device->control = usbh_serial_control;
-#endif
- device->user_data = data;
-
- /* register a character device */
- ret = rt_device_register(device, serial->name, RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX | RT_DEVICE_FLAG_REMOVABLE);
-
-#ifdef RT_USING_POSIX_DEVIO
- /* set fops */
- device->fops = &usbh_serial_fops;
-#endif
- rt_ringbuffer_init(&serial->rx_rb, serial->rx_rb_buffer, sizeof(serial->rx_rb_buffer));
-
- return ret;
-}
-
-void usbh_serial_unregister(struct usbh_serial *serial)
-{
- RT_ASSERT(serial != NULL);
-
- rt_device_unregister(&serial->parent);
-
- if (serial->type == USBH_SERIAL_TYPE_CDC_ACM) {
- usbh_serial_cdc_acm_free(serial);
- } else {
- usbh_serial_free(serial);
- }
-}
-
-#if defined(PKG_CHERRYUSB_HOST_CDC_ACM) || defined(RT_CHERRYUSB_HOST_CDC_ACM)
-void usbh_cdc_acm_callback(void *arg, int nbytes)
-{
- struct usbh_cdc_acm *cdc_acm_class = (struct usbh_cdc_acm *)arg;
- struct usbh_serial *serial;
- int ret;
- struct usbh_urb *urb = &cdc_acm_class->bulkin_urb;
-
- if (nbytes > 0) {
- serial = (struct usbh_serial *)cdc_acm_class->user_data;
- rt_ringbuffer_put(&serial->rx_rb, g_usbh_serial_cdc_acm_rx_buf[serial->minor], nbytes);
-
- if (serial->parent.rx_indicate) {
- serial->parent.rx_indicate(&serial->parent, nbytes);
- }
-
- usbh_bulk_urb_fill(urb, cdc_acm_class->hport, cdc_acm_class->bulkin, g_usbh_serial_cdc_acm_rx_buf[serial->minor], sizeof(g_usbh_serial_cdc_acm_rx_buf[serial->minor]), 0, usbh_cdc_acm_callback, cdc_acm_class);
- ret = usbh_submit_urb(urb);
- if (ret < 0) {
- USB_LOG_ERR("usbh_submit_urb failed: %d\n", ret);
- }
- }
-}
-
-void usbh_cdc_acm_run(struct usbh_cdc_acm *cdc_acm_class)
-{
- struct usbh_serial *serial;
- int ret;
- struct usbh_urb *urb = &cdc_acm_class->bulkin_urb;
-
- serial = usbh_serial_cdc_acm_alloc(USBH_SERIAL_TYPE_CDC_ACM);
- cdc_acm_class->user_data = serial;
-
- usbh_serial_register(serial, cdc_acm_class);
-
- struct cdc_line_coding linecoding;
- linecoding.dwDTERate = 115200;
- linecoding.bDataBits = 8;
- linecoding.bParityType = 0;
- linecoding.bCharFormat = 0;
- usbh_cdc_acm_set_line_coding(cdc_acm_class, &linecoding);
-
- usbh_bulk_urb_fill(urb, cdc_acm_class->hport, cdc_acm_class->bulkin, g_usbh_serial_cdc_acm_rx_buf[serial->minor], sizeof(g_usbh_serial_cdc_acm_rx_buf[serial->minor]), 0, usbh_cdc_acm_callback, cdc_acm_class);
- ret = usbh_submit_urb(urb);
- if (ret < 0) {
- USB_LOG_ERR("usbh_submit_urb failed: %d\n", ret);
- usbh_serial_unregister(serial);
- return;
- }
-}
-
-void usbh_cdc_acm_stop(struct usbh_cdc_acm *cdc_acm_class)
-{
- struct usbh_serial *serial;
-
- serial = (struct usbh_serial *)cdc_acm_class->user_data;
- usbh_serial_unregister(serial);
-}
-#endif
-
-#if defined(PKG_CHERRYUSB_HOST_FTDI) || defined(RT_CHERRYUSB_HOST_FTDI)
-void usbh_ftdi_callback(void *arg, int nbytes)
-{
- struct usbh_ftdi *ftdi_class = (struct usbh_ftdi *)arg;
- struct usbh_serial *serial;
- int ret;
- struct usbh_urb *urb = &ftdi_class->bulkin_urb;
-
- if (nbytes >= 2) {
- serial = (struct usbh_serial *)ftdi_class->user_data;
-
- nbytes -= 2; // Skip the first two bytes (header)
- rt_ringbuffer_put(&serial->rx_rb, &g_usbh_serial_vendor_rx_buf[serial->minor][2], nbytes);
-
- if (serial->parent.rx_indicate && nbytes) {
- serial->parent.rx_indicate(&serial->parent, nbytes);
- }
-
- usbh_bulk_urb_fill(urb, ftdi_class->hport, ftdi_class->bulkin, g_usbh_serial_vendor_rx_buf[serial->minor], sizeof(g_usbh_serial_vendor_rx_buf[serial->minor]), 0, usbh_ftdi_callback, ftdi_class);
- ret = usbh_submit_urb(urb);
- if (ret < 0) {
- USB_LOG_ERR("usbh_submit_urb failed: %d\n", ret);
- }
- }
-}
-
-void usbh_ftdi_run(struct usbh_ftdi *ftdi_class)
-{
- struct usbh_serial *serial;
- int ret;
- struct usbh_urb *urb = &ftdi_class->bulkin_urb;
-
- serial = usbh_serial_alloc(USBH_SERIAL_TYPE_FTDI);
- ftdi_class->user_data = serial;
-
- usbh_serial_register(serial, ftdi_class);
-
- struct cdc_line_coding linecoding;
- linecoding.dwDTERate = 115200;
- linecoding.bDataBits = 8;
- linecoding.bParityType = 0;
- linecoding.bCharFormat = 0;
- usbh_ftdi_set_line_coding(ftdi_class, &linecoding);
-
- usbh_bulk_urb_fill(urb, ftdi_class->hport, ftdi_class->bulkin, g_usbh_serial_vendor_rx_buf[serial->minor], sizeof(g_usbh_serial_vendor_rx_buf[serial->minor]), 0, usbh_ftdi_callback, ftdi_class);
- ret = usbh_submit_urb(urb);
- if (ret < 0) {
- USB_LOG_ERR("usbh_submit_urb failed: %d\n", ret);
- usbh_serial_unregister(serial);
- return;
- }
-}
-
-void usbh_ftdi_stop(struct usbh_ftdi *ftdi_class)
-{
- struct usbh_serial *serial;
-
- serial = (struct usbh_serial *)ftdi_class->user_data;
- usbh_serial_unregister(serial);
-}
-#endif
-
-#if defined(PKG_CHERRYUSB_HOST_CH34X) || defined(RT_CHERRYUSB_HOST_CH34X)
-void usbh_ch34x_callback(void *arg, int nbytes)
-{
- struct usbh_ch34x *ch34x_class = (struct usbh_ch34x *)arg;
- struct usbh_serial *serial;
- int ret;
- struct usbh_urb *urb = &ch34x_class->bulkin_urb;
-
- if (nbytes > 0) {
- serial = (struct usbh_serial *)ch34x_class->user_data;
- rt_ringbuffer_put(&serial->rx_rb, g_usbh_serial_vendor_rx_buf[serial->minor], nbytes);
-
- if (serial->parent.rx_indicate) {
- serial->parent.rx_indicate(&serial->parent, nbytes);
- }
-
- usbh_bulk_urb_fill(urb, ch34x_class->hport, ch34x_class->bulkin, g_usbh_serial_vendor_rx_buf[serial->minor], sizeof(g_usbh_serial_vendor_rx_buf[serial->minor]), 0, usbh_ch34x_callback, ch34x_class);
- ret = usbh_submit_urb(urb);
- if (ret < 0) {
- USB_LOG_ERR("usbh_submit_urb failed: %d\n", ret);
- }
- }
-}
-
-void usbh_ch34x_run(struct usbh_ch34x *ch34x_class)
-{
- struct usbh_serial *serial;
- int ret;
- struct usbh_urb *urb = &ch34x_class->bulkin_urb;
-
- serial = usbh_serial_alloc(USBH_SERIAL_TYPE_CH34X);
- ch34x_class->user_data = serial;
-
- usbh_serial_register(serial, ch34x_class);
-
- struct cdc_line_coding linecoding;
- linecoding.dwDTERate = 115200;
- linecoding.bDataBits = 8;
- linecoding.bParityType = 0;
- linecoding.bCharFormat = 0;
- usbh_ch34x_set_line_coding(ch34x_class, &linecoding);
-
- usbh_bulk_urb_fill(urb, ch34x_class->hport, ch34x_class->bulkin, g_usbh_serial_vendor_rx_buf[serial->minor], sizeof(g_usbh_serial_vendor_rx_buf[serial->minor]), 0, usbh_ch34x_callback, ch34x_class);
- ret = usbh_submit_urb(urb);
- if (ret < 0) {
- USB_LOG_ERR("usbh_submit_urb failed: %d\n", ret);
- usbh_serial_unregister(serial);
- return;
- }
-}
-
-void usbh_ch34x_stop(struct usbh_ch34x *ch34x_class)
-{
- struct usbh_serial *serial;
-
- serial = (struct usbh_serial *)ch34x_class->user_data;
- usbh_serial_unregister(serial);
-}
-#endif
-
-#if defined(PKG_CHERRYUSB_HOST_CP210X) || defined(RT_CHERRYUSB_HOST_CP210X)
-void usbh_cp210x_callback(void *arg, int nbytes)
-{
- struct usbh_cp210x *cp210x_class = (struct usbh_cp210x *)arg;
- struct usbh_serial *serial;
- int ret;
- struct usbh_urb *urb = &cp210x_class->bulkin_urb;
-
- if (nbytes > 0) {
- serial = (struct usbh_serial *)cp210x_class->user_data;
- rt_ringbuffer_put(&serial->rx_rb, g_usbh_serial_vendor_rx_buf[serial->minor], nbytes);
-
- if (serial->parent.rx_indicate) {
- serial->parent.rx_indicate(&serial->parent, nbytes);
- }
-
- usbh_bulk_urb_fill(urb, cp210x_class->hport, cp210x_class->bulkin, g_usbh_serial_vendor_rx_buf[serial->minor], sizeof(g_usbh_serial_vendor_rx_buf[serial->minor]), 0, usbh_cp210x_callback, cp210x_class);
- ret = usbh_submit_urb(urb);
- if (ret < 0) {
- USB_LOG_ERR("usbh_submit_urb failed: %d\n", ret);
- }
- }
-}
-
-void usbh_cp210x_run(struct usbh_cp210x *cp210x_class)
-{
- struct usbh_serial *serial;
- int ret;
- struct usbh_urb *urb = &cp210x_class->bulkin_urb;
-
- serial = usbh_serial_alloc(USBH_SERIAL_TYPE_CP210X);
- cp210x_class->user_data = serial;
-
- usbh_serial_register(serial, cp210x_class);
-
- struct cdc_line_coding linecoding;
- linecoding.dwDTERate = 115200;
- linecoding.bDataBits = 8;
- linecoding.bParityType = 0;
- linecoding.bCharFormat = 0;
- usbh_cp210x_set_line_coding(cp210x_class, &linecoding);
-
- usbh_bulk_urb_fill(urb, cp210x_class->hport, cp210x_class->bulkin, g_usbh_serial_vendor_rx_buf[serial->minor], sizeof(g_usbh_serial_vendor_rx_buf[serial->minor]), 0, usbh_cp210x_callback, cp210x_class);
- ret = usbh_submit_urb(urb);
- if (ret < 0) {
- USB_LOG_ERR("usbh_submit_urb failed: %d\n", ret);
- usbh_serial_unregister(serial);
- return;
- }
-}
-
-void usbh_cp210x_stop(struct usbh_cp210x *cp210x_class)
-{
- struct usbh_serial *serial;
-
- serial = (struct usbh_serial *)cp210x_class->user_data;
- usbh_serial_unregister(serial);
-}
-#endif
-
-#if defined(PKG_CHERRYUSB_HOST_PL2303) || defined(RT_CHERRYUSB_HOST_PL2303)
-void usbh_pl2303_callback(void *arg, int nbytes)
-{
- struct usbh_pl2303 *pl2303_class = (struct usbh_pl2303 *)arg;
- struct usbh_serial *serial;
- int ret;
- struct usbh_urb *urb = &pl2303_class->bulkin_urb;
-
- if (nbytes > 0) {
- serial = (struct usbh_serial *)pl2303_class->user_data;
- rt_ringbuffer_put(&serial->rx_rb, g_usbh_serial_vendor_rx_buf[serial->minor], nbytes);
-
- if (serial->parent.rx_indicate) {
- serial->parent.rx_indicate(&serial->parent, nbytes);
- }
-
- usbh_bulk_urb_fill(urb, pl2303_class->hport, pl2303_class->bulkin, g_usbh_serial_vendor_rx_buf[serial->minor], sizeof(g_usbh_serial_vendor_rx_buf[serial->minor]), 0, usbh_pl2303_callback, pl2303_class);
- ret = usbh_submit_urb(urb);
- if (ret < 0) {
- USB_LOG_ERR("usbh_submit_urb failed: %d\n", ret);
- }
- }
-}
-
-void usbh_pl2303_run(struct usbh_pl2303 *pl2303_class)
-{
- struct usbh_serial *serial;
- int ret;
- struct usbh_urb *urb = &pl2303_class->bulkin_urb;
-
- serial = usbh_serial_alloc(USBH_SERIAL_TYPE_PL2303);
- pl2303_class->user_data = serial;
-
- usbh_serial_register(serial, pl2303_class);
-
- struct cdc_line_coding linecoding;
- linecoding.dwDTERate = 115200;
- linecoding.bDataBits = 8;
- linecoding.bParityType = 0;
- linecoding.bCharFormat = 0;
- usbh_pl2303_set_line_coding(pl2303_class, &linecoding);
-
- usbh_bulk_urb_fill(urb, pl2303_class->hport, pl2303_class->bulkin, g_usbh_serial_vendor_rx_buf[serial->minor], sizeof(g_usbh_serial_vendor_rx_buf[serial->minor]), 0, usbh_pl2303_callback, pl2303_class);
- ret = usbh_submit_urb(urb);
- if (ret < 0) {
- USB_LOG_ERR("usbh_submit_urb failed: %d\n", ret);
- usbh_serial_unregister(serial);
- return;
- }
-}
-
-void usbh_pl2303_stop(struct usbh_pl2303 *pl2303_class)
-{
- struct usbh_serial *serial;
-
- serial = (struct usbh_serial *)pl2303_class->user_data;
- usbh_serial_unregister(serial);
-}
-#endif
diff --git a/tests/bouffalolab/inc/usb_config.h b/tests/bouffalolab/inc/usb_config.h
index d158eb92..01ea6360 100644
--- a/tests/bouffalolab/inc/usb_config.h
+++ b/tests/bouffalolab/inc/usb_config.h
@@ -158,7 +158,7 @@
#define CONFIG_USBHOST_MAX_INTF_ALTSETTINGS 2
#define CONFIG_USBHOST_MAX_ENDPOINTS 4
-#define CONFIG_USBHOST_MAX_CDC_ACM_CLASS 4
+#define CONFIG_USBHOST_MAX_SERIAL_CLASS 4
#define CONFIG_USBHOST_MAX_HID_CLASS 4
#define CONFIG_USBHOST_MAX_MSC_CLASS 2
#define CONFIG_USBHOST_MAX_AUDIO_CLASS 1
@@ -193,6 +193,10 @@
#define CONFIG_USBHOST_MSC_TIMEOUT 5000
#endif
+#ifndef CONFIG_USBHOST_SERIAL_RX_SIZE
+#define CONFIG_USBHOST_SERIAL_RX_SIZE 2048
+#endif
+
/* This parameter affects usb performance, and depends on (TCP_WND)tcp eceive windows size,
* you can change to 2K ~ 16K and must be larger than TCP RX windows size in order to avoid being overflow.
*/
diff --git a/tests/hpmicro/inc/usb_config.h b/tests/hpmicro/inc/usb_config.h
index 19067761..9a130183 100644
--- a/tests/hpmicro/inc/usb_config.h
+++ b/tests/hpmicro/inc/usb_config.h
@@ -47,7 +47,7 @@
#define USBD_MAX_POWER 200
/* attribute data into no cache ram */
-#define USB_NOCACHE_RAM_SECTION __attribute__((section(".fast_ram.non_init")))
+#define USB_NOCACHE_RAM_SECTION __attribute__((section(".noncacheable.non_init")))
/* use usb_memcpy default for high performance but cost more flash memory.
* And, arm libc has a bug that memcpy() may cause data misalignment when the size is not a multiple of 4.
@@ -174,7 +174,7 @@
#define CONFIG_USBHOST_MAX_INTF_ALTSETTINGS 2
#define CONFIG_USBHOST_MAX_ENDPOINTS 4
-#define CONFIG_USBHOST_MAX_CDC_ACM_CLASS 4
+#define CONFIG_USBHOST_MAX_SERIAL_CLASS 4
#define CONFIG_USBHOST_MAX_HID_CLASS 4
#define CONFIG_USBHOST_MAX_MSC_CLASS 2
#define CONFIG_USBHOST_MAX_AUDIO_CLASS 1
@@ -209,6 +209,10 @@
#define CONFIG_USBHOST_MSC_TIMEOUT 5000
#endif
+#ifndef CONFIG_USBHOST_SERIAL_RX_SIZE
+#define CONFIG_USBHOST_SERIAL_RX_SIZE 2048
+#endif
+
/* This parameter affects usb performance, and depends on (TCP_WND)tcp eceive windows size,
* you can change to 2K ~ 16K and must be larger than TCP RX windows size in order to avoid being overflow.
*/
diff --git a/tests/hpmicro/src/main.c b/tests/hpmicro/src/main.c
index b265352c..e180d834 100644
--- a/tests/hpmicro/src/main.c
+++ b/tests/hpmicro/src/main.c
@@ -17,6 +17,7 @@
#include "hpm_gpio_drv.h"
#include "shell.h"
#include "usbh_core.h"
+#include "usbh_serial.h"
#include "lwip/tcpip.h"
#ifdef CONFIG_USB_EHCI_ISO
#include "usbh_uvc_stream.h"
@@ -116,6 +117,7 @@ static void task_start(void *param)
}
CSH_CMD_EXPORT(lsusb, );
+CSH_CMD_EXPORT(usbh_serial, );
#ifdef CONFIG_USB_EHCI_ISO
// clang-format off