From da2263728aba07a28d9b6946b7acb6b0a0a97bb1 Mon Sep 17 00:00:00 2001 From: sakumisu <1203593632@qq.com> Date: Fri, 12 Dec 2025 22:45:10 +0800 Subject: [PATCH] refactor(serial): add host serial framework Signed-off-by: sakumisu <1203593632@qq.com> --- CMakeLists.txt | 5 + Kconfig | 25 +- Kconfig.rtt | 25 +- Kconfig.rttpkg | 24 +- README.md | 5 +- README_zh.md | 5 +- SConscript | 8 +- cherryusb.cmake | 27 +- cherryusb_config_template.h | 6 +- class/cdc/usbh_cdc_acm.c | 294 ---------- class/cdc/usbh_cdc_acm.h | 50 -- class/serial/usbh_cdc_acm.c | 266 +++++++++ class/serial/usbh_cdc_acm.h | 19 + class/serial/usbh_ch34x.c | 410 +++++++++++++ class/serial/usbh_ch34x.h | 56 ++ class/serial/usbh_cp210x.c | 510 ++++++++++++++++ class/serial/usbh_cp210x.h | 187 ++++++ class/serial/usbh_ftdi.c | 407 +++++++++++++ class/serial/usbh_ftdi.h | 341 +++++++++++ class/serial/usbh_gsm.c | 127 ++++ class/serial/usbh_pl2303.c | 726 +++++++++++++++++++++++ class/serial/usbh_pl2303.h | 43 ++ class/serial/usbh_serial.c | 719 +++++++++++++++++++++++ class/serial/usbh_serial.h | 179 ++++++ class/vendor/serial/usbh_ch34x.c | 379 ------------ class/vendor/serial/usbh_ch34x.h | 76 --- class/vendor/serial/usbh_cp210x.c | 328 ----------- class/vendor/serial/usbh_cp210x.h | 73 --- class/vendor/serial/usbh_ftdi.c | 510 ---------------- class/vendor/serial/usbh_ftdi.h | 96 --- class/vendor/serial/usbh_pl2303.c | 449 -------------- class/vendor/serial/usbh_pl2303.h | 62 -- common/usb_util.h | 17 + demo/usb_host.c | 155 +++-- docs/source/api/api_host.rst | 105 +++- docs/source/demo/usbh_serial.rst | 94 ++- osal/idf/usb_config.h | 8 +- platform/rtthread/usb_msh.c | 6 + platform/rtthread/usbh_rtserial.c | 209 +++++++ platform/rtthread/usbh_serial.c | 899 ----------------------------- tests/bouffalolab/inc/usb_config.h | 6 +- tests/hpmicro/inc/usb_config.h | 8 +- tests/hpmicro/src/main.c | 2 + 43 files changed, 4649 insertions(+), 3297 deletions(-) delete mode 100644 class/cdc/usbh_cdc_acm.c delete mode 100644 class/cdc/usbh_cdc_acm.h create mode 100644 class/serial/usbh_cdc_acm.c create mode 100644 class/serial/usbh_cdc_acm.h create mode 100644 class/serial/usbh_ch34x.c create mode 100644 class/serial/usbh_ch34x.h create mode 100644 class/serial/usbh_cp210x.c create mode 100644 class/serial/usbh_cp210x.h create mode 100644 class/serial/usbh_ftdi.c create mode 100644 class/serial/usbh_ftdi.h create mode 100644 class/serial/usbh_gsm.c create mode 100644 class/serial/usbh_pl2303.c create mode 100644 class/serial/usbh_pl2303.h create mode 100644 class/serial/usbh_serial.c create mode 100644 class/serial/usbh_serial.h delete mode 100644 class/vendor/serial/usbh_ch34x.c delete mode 100644 class/vendor/serial/usbh_ch34x.h delete mode 100644 class/vendor/serial/usbh_cp210x.c delete mode 100644 class/vendor/serial/usbh_cp210x.h delete mode 100644 class/vendor/serial/usbh_ftdi.c delete mode 100644 class/vendor/serial/usbh_ftdi.h delete mode 100644 class/vendor/serial/usbh_pl2303.c delete mode 100644 class/vendor/serial/usbh_pl2303.h create mode 100644 platform/rtthread/usbh_rtserial.c delete mode 100644 platform/rtthread/usbh_serial.c 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 [options]...\r\n" + "\r\n" + "-b set serial baudrate\r\n" + "-t 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) -{ - //struct usbh_cdc_acm *cdc_acm_class = (struct usbh_cdc_acm *)arg; - - if (nbytes > 0) { - for (size_t i = 0; i < nbytes; i++) { - USB_LOG_RAW("0x%02x ", cdc_buffer[i]); - } - USB_LOG_RAW("nbytes:%d\r\n", (unsigned int)nbytes); - } -} - -static void usbh_cdc_acm_thread(CONFIG_USB_OSAL_THREAD_SET_ARGV) +static void usbh_serial_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 *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; + } /* 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 +#include + +#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 -#include - -#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 -#include -#include -#include -#include - -#ifdef RT_USING_POSIX_TERMIOS -#include -#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