refactor(serial): add host serial framework

Signed-off-by: sakumisu <1203593632@qq.com>
This commit is contained in:
sakumisu
2025-12-12 22:45:10 +08:00
parent 707e865627
commit da2263728a
43 changed files with 4649 additions and 3297 deletions

View File

@@ -26,6 +26,7 @@ if(BL_SDK_BASE)
set(CONFIG_CHERRYUSB_HOST_CP210X 1) set(CONFIG_CHERRYUSB_HOST_CP210X 1)
set(CONFIG_CHERRYUSB_HOST_FTDI 1) set(CONFIG_CHERRYUSB_HOST_FTDI 1)
set(CONFIG_CHERRYUSB_HOST_PL2303 1) set(CONFIG_CHERRYUSB_HOST_PL2303 1)
set(CONFIG_CHERRYUSB_HOST_GSM 1)
set(CONFIG_CHERRYUSB_DEVICE_BL 1) set(CONFIG_CHERRYUSB_DEVICE_BL 1)
set(CONFIG_CHERRYUSB_HOST_EHCI_BL 1) set(CONFIG_CHERRYUSB_HOST_EHCI_BL 1)
@@ -139,6 +140,9 @@ elseif(ESP_PLATFORM)
if(CONFIG_CHERRYUSB_HOST_PL2303) if(CONFIG_CHERRYUSB_HOST_PL2303)
target_link_libraries(${COMPONENT_LIB} INTERFACE "-u pl2303_class_info") target_link_libraries(${COMPONENT_LIB} INTERFACE "-u pl2303_class_info")
endif() endif()
if(CONFIG_CHERRYUSB_HOST_GSM)
target_link_libraries(${COMPONENT_LIB} INTERFACE "-u gsm_class_info")
endif()
endif() endif()
if(CONFIG_CHERRYUSB) if(CONFIG_CHERRYUSB)
@@ -194,6 +198,7 @@ elseif(HPM_SDK_BASE)
set(CONFIG_CHERRYUSB_HOST_CP210X 1) set(CONFIG_CHERRYUSB_HOST_CP210X 1)
set(CONFIG_CHERRYUSB_HOST_FTDI 1) set(CONFIG_CHERRYUSB_HOST_FTDI 1)
set(CONFIG_CHERRYUSB_HOST_PL2303 1) set(CONFIG_CHERRYUSB_HOST_PL2303 1)
set(CONFIG_CHERRYUSB_HOST_GSM 1)
set(CONFIG_CHERRYUSB_DEVICE_HPM 1) set(CONFIG_CHERRYUSB_DEVICE_HPM 1)
set(CONFIG_CHERRYUSB_HOST_EHCI_HPM 1) set(CONFIG_CHERRYUSB_HOST_EHCI_HPM 1)

25
Kconfig
View File

@@ -299,6 +299,7 @@ if CHERRYUSB
config CHERRYUSB_HOST_CDC_ACM config CHERRYUSB_HOST_CDC_ACM
bool bool
prompt "Enable usb cdc acm driver" prompt "Enable usb cdc acm driver"
select USBHOST_SERIAL
default n default n
config CHERRYUSB_HOST_HID config CHERRYUSB_HOST_HID
@@ -360,21 +361,31 @@ if CHERRYUSB
config CHERRYUSB_HOST_FTDI config CHERRYUSB_HOST_FTDI
bool bool
prompt "Enable usb ftdi driver" prompt "Enable usb ftdi driver"
select USBHOST_SERIAL
default n default n
config CHERRYUSB_HOST_CH34X config CHERRYUSB_HOST_CH34X
bool bool
prompt "Enable usb ch34x driver" prompt "Enable usb ch34x driver"
select USBHOST_SERIAL
default n default n
config CHERRYUSB_HOST_CP210X config CHERRYUSB_HOST_CP210X
bool bool
prompt "Enable usb cp210x driver" prompt "Enable usb cp210x driver"
select USBHOST_SERIAL
default n default n
config CHERRYUSB_HOST_PL2303 config CHERRYUSB_HOST_PL2303
bool bool
prompt "Enable usb pl2303 driver" 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 default n
config CHERRYUSB_HOST_AOA config CHERRYUSB_HOST_AOA
@@ -382,6 +393,9 @@ if CHERRYUSB
prompt "Enable usb aoa driver" prompt "Enable usb aoa driver"
default n default n
config USBHOST_SERIAL
bool
config USBHOST_PLATFORM_CDC_ECM config USBHOST_PLATFORM_CDC_ECM
bool bool
@@ -417,12 +431,17 @@ if CHERRYUSB
prompt "Set host control transfer timeout, unit is ms" prompt "Set host control transfer timeout, unit is ms"
default 500 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" menu "Select USB host template, please select class driver first"
config TEST_USBH_CDC_ACM config TEST_USBH_SERIAL
int int
prompt "demo for test cdc acm" prompt "demo for test serial"
default 0 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 config TEST_USBH_HID
int int
prompt "demo for test hid" prompt "demo for test hid"

View File

@@ -315,6 +315,7 @@ if RT_USING_CHERRYUSB
config RT_CHERRYUSB_HOST_CDC_ACM config RT_CHERRYUSB_HOST_CDC_ACM
bool bool
prompt "Enable usb cdc acm driver" prompt "Enable usb cdc acm driver"
select CONFIG_USBHOST_SERIAL
default n default n
config RT_CHERRYUSB_HOST_HID config RT_CHERRYUSB_HOST_HID
@@ -382,23 +383,36 @@ if RT_USING_CHERRYUSB
config RT_CHERRYUSB_HOST_FTDI config RT_CHERRYUSB_HOST_FTDI
bool bool
prompt "Enable usb ftdi driver" prompt "Enable usb ftdi driver"
select CONFIG_USBHOST_SERIAL
default n default n
config RT_CHERRYUSB_HOST_CH34X config RT_CHERRYUSB_HOST_CH34X
bool bool
prompt "Enable usb ch34x driver" prompt "Enable usb ch34x driver"
select CONFIG_USBHOST_SERIAL
default n default n
config RT_CHERRYUSB_HOST_CP210X config RT_CHERRYUSB_HOST_CP210X
bool bool
prompt "Enable usb cp210x driver" prompt "Enable usb cp210x driver"
select CONFIG_USBHOST_SERIAL
default n default n
config RT_CHERRYUSB_HOST_PL2303 config RT_CHERRYUSB_HOST_PL2303
bool bool
prompt "Enable usb pl2303 driver" prompt "Enable usb pl2303 driver"
select CONFIG_USBHOST_SERIAL
default n 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 config CONFIG_USBHOST_PLATFORM_CDC_ECM
bool bool
@@ -434,6 +448,11 @@ if RT_USING_CHERRYUSB
prompt "Set host control transfer timeout, unit is ms" prompt "Set host control transfer timeout, unit is ms"
default 500 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 config RT_LWIP_PBUF_POOL_BUFSIZE
int "The size of each pbuf in the pbuf pool" int "The size of each pbuf in the pbuf pool"
range 1500 2000 range 1500 2000
@@ -445,11 +464,11 @@ if RT_USING_CHERRYUSB
default "/" default "/"
menu "Select USB host template, please select class driver first" menu "Select USB host template, please select class driver first"
config CONFIG_TEST_USBH_CDC_ACM config CONFIG_TEST_USBH_SERIAL
int 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 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 config CONFIG_TEST_USBH_HID
int int
prompt "demo for test hid" prompt "demo for test hid"

View File

@@ -381,23 +381,36 @@ if PKG_USING_CHERRYUSB
config PKG_CHERRYUSB_HOST_FTDI config PKG_CHERRYUSB_HOST_FTDI
bool bool
prompt "Enable usb ftdi driver" prompt "Enable usb ftdi driver"
select CONFIG_USBHOST_SERIAL
default n default n
config PKG_CHERRYUSB_HOST_CH34X config PKG_CHERRYUSB_HOST_CH34X
bool bool
prompt "Enable usb ch34x driver" prompt "Enable usb ch34x driver"
select CONFIG_USBHOST_SERIAL
default n default n
config PKG_CHERRYUSB_HOST_CP210X config PKG_CHERRYUSB_HOST_CP210X
bool bool
prompt "Enable usb cp210x driver" prompt "Enable usb cp210x driver"
select CONFIG_USBHOST_SERIAL
default n default n
config PKG_CHERRYUSB_HOST_PL2303 config PKG_CHERRYUSB_HOST_PL2303
bool bool
prompt "Enable usb pl2303 driver" prompt "Enable usb pl2303 driver"
select CONFIG_USBHOST_SERIAL
default n 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 config CONFIG_USBHOST_PLATFORM_CDC_ECM
bool bool
@@ -433,6 +446,11 @@ if PKG_USING_CHERRYUSB
prompt "Set host control transfer timeout, unit is ms" prompt "Set host control transfer timeout, unit is ms"
default 500 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 config RT_LWIP_PBUF_POOL_BUFSIZE
int "The size of each pbuf in the pbuf pool" int "The size of each pbuf in the pbuf pool"
range 1500 2000 range 1500 2000
@@ -444,11 +462,11 @@ if PKG_USING_CHERRYUSB
default "/" default "/"
menu "Select USB host template, please select class driver first" menu "Select USB host template, please select class driver first"
config CONFIG_TEST_USBH_CDC_ACM config CONFIG_TEST_USBH_SERIAL
int 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 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 config CONFIG_TEST_USBH_HID
int int
prompt "demo for test hid" prompt "demo for test hid"

View File

@@ -112,7 +112,8 @@ CherryUSB Host Stack has the following functions:
- Support USB Audio CLASS (UAC1.0) - Support USB Audio CLASS (UAC1.0)
- Support Remote NDIS (RNDIS) - Support Remote NDIS (RNDIS)
- Support USB Bluetooth class (support nimble and zephyr bluetooth stack, support **CLASS:0xE0** or vendor class like cdc acm) - 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 USB modeswitch
- Support Android Open Accessory - Support Android Open Accessory
- Support multi host with the same USB IP - 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: 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_HID_CLASS 4
#define CONFIG_USBHOST_MAX_MSC_CLASS 2 #define CONFIG_USBHOST_MAX_MSC_CLASS 2
#define CONFIG_USBHOST_MAX_AUDIO_CLASS 1 #define CONFIG_USBHOST_MAX_AUDIO_CLASS 1

View File

@@ -112,7 +112,8 @@ CherryUSB Host 协议栈当前实现以下功能:
- Support USB Audio CLASS (UAC1.0) - Support USB Audio CLASS (UAC1.0)
- 支持 Remote NDIS (RNDIS) - 支持 Remote NDIS (RNDIS)
- 支持 USB Bluetooth (支持 nimble and zephyr bluetooth 协议栈,支持 **CLASS: 0xE0** 或者厂家自定义类,类似于 cdc acm 功能) - 支持 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 - 支持 USB modeswitch
- 支持 Android Open Accessory - 支持 Android Open Accessory
- 支持相同 USB IP 的多主机 - 支持相同 USB IP 的多主机
@@ -150,7 +151,7 @@ CherryUSB Host 协议栈资源占用说明GCC 10.2 with -O2关闭 log
x 受以下宏影响: 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_HID_CLASS 4
#define CONFIG_USBHOST_MAX_MSC_CLASS 2 #define CONFIG_USBHOST_MAX_MSC_CLASS 2
#define CONFIG_USBHOST_MAX_AUDIO_CLASS 1 #define CONFIG_USBHOST_MAX_AUDIO_CLASS 1

View File

@@ -14,7 +14,7 @@ path += [cwd + '/class/wireless']
path += [cwd + '/class/midi'] path += [cwd + '/class/midi']
path += [cwd + '/class/adb'] path += [cwd + '/class/adb']
path += [cwd + '/class/dfu'] path += [cwd + '/class/dfu']
path += [cwd + '/class/midi'] path += [cwd + '/class/serial']
path += [cwd + '/class/vendor/net'] path += [cwd + '/class/vendor/net']
path += [cwd + '/class/vendor/serial'] path += [cwd + '/class/vendor/serial']
path += [cwd + '/class/vendor/wifi'] 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_FTDI']) \
or GetDepend(['PKG_CHERRYUSB_HOST_CH34X']) \ or GetDepend(['PKG_CHERRYUSB_HOST_CH34X']) \
or GetDepend(['PKG_CHERRYUSB_HOST_CP210X']) \ or GetDepend(['PKG_CHERRYUSB_HOST_CP210X']) \
or GetDepend(['PKG_CHERRYUSB_HOST_PL2303']): or GetDepend(['PKG_CHERRYUSB_HOST_PL2303']) \
src += Glob('platform/rtthread/usbh_serial.c') 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']): if GetDepend('RT_USING_DFS') and GetDepend(['PKG_CHERRYUSB_HOST_MSC']):
src += Glob('platform/rtthread/usbh_dfs.c') src += Glob('platform/rtthread/usbh_dfs.c')

View File

@@ -47,8 +47,8 @@ list(
${CMAKE_CURRENT_LIST_DIR}/class/midi ${CMAKE_CURRENT_LIST_DIR}/class/midi
${CMAKE_CURRENT_LIST_DIR}/class/adb ${CMAKE_CURRENT_LIST_DIR}/class/adb
${CMAKE_CURRENT_LIST_DIR}/class/dfu ${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/net
${CMAKE_CURRENT_LIST_DIR}/class/vendor/serial
${CMAKE_CURRENT_LIST_DIR}/class/vendor/wifi ${CMAKE_CURRENT_LIST_DIR}/class/vendor/wifi
${CMAKE_CURRENT_LIST_DIR}/class/aoa ${CMAKE_CURRENT_LIST_DIR}/class/aoa
) )
@@ -156,7 +156,7 @@ if(CONFIG_CHERRYUSB_HOST)
) )
if(CONFIG_CHERRYUSB_HOST_CDC_ACM) 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() endif()
if(CONFIG_CHERRYUSB_HOST_CDC_ECM) if(CONFIG_CHERRYUSB_HOST_CDC_ECM)
list(APPEND cherryusb_srcs ${CMAKE_CURRENT_LIST_DIR}/class/cdc/usbh_cdc_ecm.c) 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) list(APPEND cherryusb_srcs ${CMAKE_CURRENT_LIST_DIR}/class/vendor/net/usbh_rtl8152.c)
endif() endif()
if(CONFIG_CHERRYUSB_HOST_CH34X) 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() endif()
if(CONFIG_CHERRYUSB_HOST_CP210X) 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() endif()
if(CONFIG_CHERRYUSB_HOST_FTDI) 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() endif()
if(CONFIG_CHERRYUSB_HOST_PL2303) 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() endif()
if(CONFIG_CHERRYUSB_HOST_AOA) if(CONFIG_CHERRYUSB_HOST_AOA)
list(APPEND cherryusb_srcs ${CMAKE_CURRENT_LIST_DIR}/class/aoa/usbh_aoa.c) list(APPEND cherryusb_srcs ${CMAKE_CURRENT_LIST_DIR}/class/aoa/usbh_aoa.c)
endif() 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 if(CONFIG_CHERRYUSB_HOST_CDC_ECM
OR CONFIG_CHERRYUSB_HOST_CDC_RNDIS OR CONFIG_CHERRYUSB_HOST_CDC_RNDIS
OR CONFIG_CHERRYUSB_HOST_CDC_NCM 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) list(APPEND cherryusb_srcs ${CMAKE_CURRENT_LIST_DIR}/port/rp2040/usb_hc_rp2040.c)
endif() 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) list(APPEND cherryusb_srcs ${CMAKE_CURRENT_LIST_DIR}/demo/usb_host.c)
endif() endif()
endif() endif()

View File

@@ -157,7 +157,7 @@
#define CONFIG_USBHOST_MAX_INTF_ALTSETTINGS 2 #define CONFIG_USBHOST_MAX_INTF_ALTSETTINGS 2
#define CONFIG_USBHOST_MAX_ENDPOINTS 4 #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_HID_CLASS 4
#define CONFIG_USBHOST_MAX_MSC_CLASS 2 #define CONFIG_USBHOST_MAX_MSC_CLASS 2
#define CONFIG_USBHOST_MAX_AUDIO_CLASS 1 #define CONFIG_USBHOST_MAX_AUDIO_CLASS 1
@@ -188,6 +188,10 @@
#define CONFIG_USBHOST_CONTROL_TRANSFER_TIMEOUT 500 #define CONFIG_USBHOST_CONTROL_TRANSFER_TIMEOUT 500
#endif #endif
#ifndef CONFIG_USBHOST_SERIAL_RX_SIZE
#define CONFIG_USBHOST_SERIAL_RX_SIZE 2048
#endif
#ifndef CONFIG_USBHOST_MSC_TIMEOUT #ifndef CONFIG_USBHOST_MSC_TIMEOUT
#define CONFIG_USBHOST_MSC_TIMEOUT 5000 #define CONFIG_USBHOST_MSC_TIMEOUT 5000
#endif #endif

View File

@@ -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
};

View File

@@ -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 */

266
class/serial/usbh_cdc_acm.c Normal file
View File

@@ -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
};

View File

@@ -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 */

410
class/serial/usbh_ch34x.c Normal file
View File

@@ -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
};

56
class/serial/usbh_ch34x.h Normal file
View File

@@ -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 */

510
class/serial/usbh_cp210x.c Normal file
View File

@@ -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
};

187
class/serial/usbh_cp210x.h Normal file
View File

@@ -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 */

407
class/serial/usbh_ftdi.c Normal file
View File

@@ -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
};

341
class/serial/usbh_ftdi.h Normal file
View File

@@ -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 */

127
class/serial/usbh_gsm.c Normal file
View File

@@ -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
};

726
class/serial/usbh_pl2303.c Normal file
View File

@@ -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
};

View File

@@ -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 */

719
class/serial/usbh_serial.c Normal file
View File

@@ -0,0 +1,719 @@
/*
* Copyright (c) 2025, sakumisu
* Copyright (c) 2025, MDLZCOOL
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "usbh_core.h"
#include "usbh_serial.h"
#undef USB_DBG_TAG
#define USB_DBG_TAG "usbh_serial"
#include "usb_log.h"
#define DEV_FORMAT_VENDOR "/dev/ttyUSB%d"
#define DEV_FORMAT_CDC_ACM "/dev/ttyACM%d"
#define CONFIG_USBHOST_MAX_SERIAL_CLASS 4
static struct usbh_serial g_serial_class[CONFIG_USBHOST_MAX_SERIAL_CLASS];
static uint32_t g_devinuse = 0;
static uint32_t g_cdcacm_devinuse = 0;
USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_serial_iobuffer[CONFIG_USBHOST_MAX_SERIAL_CLASS][USB_ALIGN_UP((USBH_SERIAL_INT_NOCACHE_OFFSET + USBH_SERIAL_INT_NOCACHE_SIZE), CONFIG_USB_ALIGN_SIZE)];
/* refer to cherryrb */
static int usbh_serial_ringbuffer_init(usbh_serial_ringbuf_t *rb, void *pool, uint32_t size)
{
if (NULL == rb) {
return -1;
}
if (NULL == pool) {
return -1;
}
if ((size < 2) || (size & (size - 1))) {
return -1;
}
rb->in = 0;
rb->out = 0;
rb->mask = size - 1;
rb->pool = pool;
return 0;
}
static void usbh_serial_ringbuffer_reset(usbh_serial_ringbuf_t *rb)
{
rb->in = 0;
rb->out = 0;
}
static uint32_t usbh_serial_ringbuffer_get_used(usbh_serial_ringbuf_t *rb)
{
return rb->in - rb->out;
}
static uint32_t usbh_serial_ringbuffer_write(usbh_serial_ringbuf_t *rb, void *data, uint32_t size)
{
uint32_t unused;
uint32_t offset;
uint32_t remain;
unused = (rb->mask + 1) - (rb->in - rb->out);
if (size > unused) {
size = unused;
}
offset = rb->in & rb->mask;
remain = rb->mask + 1 - offset;
remain = remain > size ? size : remain;
memcpy(((uint8_t *)(rb->pool)) + offset, data, remain);
memcpy(rb->pool, (uint8_t *)data + remain, size - remain);
rb->in += size;
return size;
}
static uint32_t usbh_serial_ringbuffer_peek(usbh_serial_ringbuf_t *rb, void *data, uint32_t size)
{
uint32_t used;
uint32_t offset;
uint32_t remain;
used = rb->in - rb->out;
if (size > used) {
size = used;
}
offset = rb->out & rb->mask;
remain = rb->mask + 1 - offset;
remain = remain > size ? size : remain;
memcpy(data, ((uint8_t *)(rb->pool)) + offset, remain);
memcpy((uint8_t *)data + remain, rb->pool, size - remain);
return size;
}
static uint32_t usbh_serial_ringbuffer_read(usbh_serial_ringbuf_t *rb, void *data, uint32_t size)
{
size = usbh_serial_ringbuffer_peek(rb, data, size);
rb->out += size;
return size;
}
static struct usbh_serial *usbh_serial_alloc(bool is_cdcacm)
{
uint8_t devno;
uint8_t devno2;
for (devno = 0; devno < CONFIG_USBHOST_MAX_SERIAL_CLASS; devno++) {
if ((g_devinuse & (1U << devno)) == 0) {
g_devinuse |= (1U << devno);
memset(&g_serial_class[devno], 0, sizeof(struct usbh_serial));
g_serial_class[devno].minor = devno;
g_serial_class[devno].cdc_minor = -1;
g_serial_class[devno].iobuffer = g_serial_iobuffer[devno];
g_serial_class[devno].rx_complete_sem = usb_osal_sem_create(0);
if (is_cdcacm) {
for (devno2 = 0; devno2 < CONFIG_USBHOST_MAX_SERIAL_CLASS; devno2++) {
if ((g_cdcacm_devinuse & (1U << devno2)) == 0) {
g_cdcacm_devinuse |= (1U << devno2);
g_serial_class[devno].cdc_minor = devno2;
return &g_serial_class[devno];
}
}
g_devinuse &= ~(1U << devno);
return NULL;
} else {
return &g_serial_class[devno];
}
}
}
return NULL;
}
static void usbh_serial_free(struct usbh_serial *serial)
{
uint8_t devno = serial->minor;
if (devno < 32) {
g_devinuse &= ~(1U << devno);
}
if (serial->cdc_minor >= 0) {
g_cdcacm_devinuse &= ~(1U << serial->cdc_minor);
}
if (g_serial_class[devno].rx_complete_sem) {
usb_osal_sem_delete(g_serial_class[devno].rx_complete_sem);
}
}
static void usbh_serial_callback(void *arg, int nbytes)
{
struct usbh_serial *serial = (struct usbh_serial *)arg;
int ret;
if (nbytes >= serial->driver->ignore_rx_header) {
usbh_serial_ringbuffer_write(&serial->rx_rb,
&serial->iobuffer[USBH_SERIAL_RX_NOCACHE_OFFSET + serial->driver->ignore_rx_header],
(nbytes - serial->driver->ignore_rx_header));
/* resubmit the read urb */
usbh_bulk_urb_fill(&serial->bulkin_urb, serial->hport, serial->bulkin, &serial->iobuffer[USBH_SERIAL_RX_NOCACHE_OFFSET], serial->bulkin->wMaxPacketSize,
0, usbh_serial_callback, serial);
ret = usbh_submit_urb(&serial->bulkin_urb);
if (ret < 0) {
USB_LOG_ERR("serial submit failed: %d\n", ret);
}
if (serial->rx_complete_callback) {
serial->rx_complete_callback(serial, nbytes - serial->driver->ignore_rx_header);
}
serial->rx_errorcode = 0;
usb_osal_sem_give(serial->rx_complete_sem);
} else {
serial->rx_errorcode = nbytes;
usb_osal_sem_give(serial->rx_complete_sem);
}
}
struct usbh_serial *usbh_serial_probe(struct usbh_hubport *hport, uint8_t intf,
const struct usbh_serial_driver *driver)
{
struct usb_endpoint_descriptor *ep_desc;
struct usbh_serial *serial;
bool is_cdcacm = false;
int ret;
if (strcmp(driver->driver_name, "cdc_acm") == 0) {
is_cdcacm = true;
}
serial = usbh_serial_alloc(is_cdcacm);
if (serial == NULL) {
USB_LOG_ERR("Fail to alloc serial class\r\n");
return NULL;
}
serial->hport = hport;
serial->intf = intf;
serial->driver = driver;
if (driver->attach) {
ret = driver->attach(serial);
if (ret < 0) {
USB_LOG_ERR("Serial attach failed: %d\r\n", ret);
usbh_serial_free(serial);
return NULL;
}
}
if (is_cdcacm) {
intf = intf + 1; /* data interface */
}
for (uint8_t i = 0; i < hport->config.intf[intf].altsetting[0].intf_desc.bNumEndpoints; i++) {
ep_desc = &hport->config.intf[intf].altsetting[0].ep[i].ep_desc;
if (USB_GET_ENDPOINT_TYPE(ep_desc->bmAttributes) == USB_ENDPOINT_TYPE_BULK) {
if (ep_desc->bEndpointAddress & 0x80) {
USBH_EP_INIT(serial->bulkin, ep_desc);
} else {
USBH_EP_INIT(serial->bulkout, ep_desc);
}
}
}
if (is_cdcacm) {
intf = intf - 1; /* data interface */
}
if (!serial->bulkin || !serial->bulkout) {
USB_LOG_ERR("Serial bulk in/out endpoint not found\r\n");
usbh_serial_free(serial);
return NULL;
}
if (is_cdcacm) {
snprintf(hport->config.intf[intf].devname, CONFIG_USBHOST_DEV_NAMELEN, DEV_FORMAT_CDC_ACM, serial->cdc_minor);
} else {
snprintf(hport->config.intf[intf].devname, CONFIG_USBHOST_DEV_NAMELEN, DEV_FORMAT_VENDOR, serial->minor);
}
hport->config.intf[intf].priv = serial;
USB_LOG_INFO("Register Serial Class: %s (%s)\r\n", hport->config.intf[intf].devname, driver->driver_name);
usbh_serial_run(serial);
return serial;
}
void usbh_serial_remove(struct usbh_serial *serial)
{
if (!serial || !serial->hport)
return;
usbh_serial_close(serial);
if (serial->driver && serial->driver->detach) {
serial->driver->detach(serial);
}
if (serial->hport->config.intf[serial->intf].priv) {
usb_osal_thread_schedule_other();
USB_LOG_INFO("Unregister Serial Class: %s (%s)\r\n", serial->hport->config.intf[serial->intf].devname, serial->driver->driver_name);
usbh_serial_stop(serial);
}
usbh_serial_free(serial);
}
struct usbh_serial *usbh_serial_open(const char *devname, uint32_t open_flags)
{
struct usbh_serial *serial;
int ret;
serial = usbh_find_class_instance(devname);
if (!serial) {
return NULL;
}
if (serial->ref_count != 0) {
USB_LOG_ERR("Device busy: %s\r\n", devname);
return NULL;
}
if (serial && serial->driver && serial->driver->open) {
ret = serial->driver->open(serial);
if (ret < 0) {
return NULL;
}
}
usbh_serial_ringbuffer_init(&serial->rx_rb, serial->rx_rb_pool, CONFIG_USBHOST_SERIAL_RX_SIZE);
serial->ref_count++;
serial->open_flags = open_flags;
return serial;
}
int usbh_serial_close(struct usbh_serial *serial)
{
if (!serial || !serial->hport) {
return -USB_ERR_INVAL;
}
if (serial->ref_count == 0) {
return 0;
}
if (serial->bulkin) {
usbh_kill_urb(&serial->bulkin_urb);
}
if (serial->bulkout) {
usbh_kill_urb(&serial->bulkout_urb);
}
if (serial && serial->driver && serial->driver->set_flow_control && serial->rtscts) {
serial->driver->set_flow_control(serial, false);
}
if (serial && serial->driver && serial->driver->close) {
serial->driver->close(serial);
}
serial->ref_count--;
serial->rtscts = false;
return 0;
}
static int usbh_serial_tiocmset(struct usbh_serial *serial, uint32_t set, uint32_t clear)
{
int ret;
uint16_t line_state;
bool dtr;
bool rts;
if (!serial || !serial->hport || !serial->hport->connected) {
return -USB_ERR_INVAL;
}
if (serial->ref_count == 0) {
return -USB_ERR_NODEV;
}
line_state = serial->line_state;
clear &= ~set; /* 'set' takes precedence over 'clear' */
if (set & USBH_SERIAL_TIOCM_DTR) {
line_state |= USBH_SERIAL_TIOCM_DTR;
}
if (set & USBH_SERIAL_TIOCM_RTS) {
line_state |= USBH_SERIAL_TIOCM_RTS;
}
if (clear & USBH_SERIAL_TIOCM_DTR) {
line_state &= ~USBH_SERIAL_TIOCM_DTR;
}
if (clear & USBH_SERIAL_TIOCM_RTS) {
line_state &= ~USBH_SERIAL_TIOCM_RTS;
}
dtr = (line_state & USBH_SERIAL_TIOCM_RTS) ? true : false;
rts = (line_state & USBH_SERIAL_TIOCM_RTS) ? true : false;
if (serial && serial->driver && serial->driver->set_line_state) {
ret = serial->driver->set_line_state(serial, dtr, rts);
} else {
return -USB_ERR_NOTSUPP;
}
serial->line_state = line_state;
return ret;
}
int usbh_serial_control(struct usbh_serial *serial, int cmd, void *arg)
{
int ret;
if (!serial || !serial->hport || !serial->hport->connected) {
return -USB_ERR_INVAL;
}
if (serial->ref_count == 0) {
return -USB_ERR_NODEV;
}
switch (cmd) {
case USBH_SERIAL_CMD_SET_ATTR: {
struct usbh_serial_termios *termios = (struct usbh_serial_termios *)arg;
struct cdc_line_coding line_coding;
line_coding.dwDTERate = termios->baudrate;
line_coding.bCharFormat = termios->stopbits;
line_coding.bParityType = termios->parity;
line_coding.bDataBits = termios->databits;
if (serial->bulkin) {
usbh_kill_urb(&serial->bulkin_urb);
}
if (serial->bulkout) {
usbh_kill_urb(&serial->bulkout_urb);
}
if (serial && serial->driver && serial->driver->set_line_coding) {
ret = serial->driver->set_line_coding(serial, &line_coding);
if (ret < 0) {
return ret;
}
} else {
return -USB_ERR_NOTSUPP;
}
memcpy(&serial->line_coding, &line_coding, sizeof(struct cdc_line_coding));
if (serial && serial->driver && serial->driver->set_flow_control) {
ret = serial->driver->set_flow_control(serial, termios->rtscts);
}
serial->rtscts = termios->rtscts;
serial->rx_timeout_ms = termios->rx_timeout;
ret = usbh_serial_tiocmset(serial, USBH_SERIAL_TIOCM_DTR | USBH_SERIAL_TIOCM_RTS, 0);
if (ret < 0) {
return ret;
}
usbh_serial_ringbuffer_reset(&serial->rx_rb);
usb_osal_sem_reset(serial->rx_complete_sem);
usbh_bulk_urb_fill(&serial->bulkin_urb, serial->hport, serial->bulkin, &serial->iobuffer[USBH_SERIAL_RX_NOCACHE_OFFSET], serial->bulkin->wMaxPacketSize,
0, usbh_serial_callback, serial);
ret = usbh_submit_urb(&serial->bulkin_urb);
return ret;
} break;
case USBH_SERIAL_CMD_GET_ATTR: {
struct usbh_serial_termios *termios = (struct usbh_serial_termios *)arg;
struct cdc_line_coding line_coding;
if (serial && serial->driver && serial->driver->get_line_coding) {
return serial->driver->get_line_coding(serial, &line_coding);
} else {
memcpy(&line_coding, &serial->line_coding, sizeof(struct cdc_line_coding));
}
termios->baudrate = line_coding.dwDTERate;
termios->stopbits = line_coding.bCharFormat;
termios->parity = line_coding.bParityType;
termios->databits = line_coding.bDataBits;
termios->rtscts = serial->rtscts;
termios->rx_timeout = serial->rx_timeout_ms;
return 0;
} break;
case USBH_SERIAL_CMD_IOCMBIS: {
uint32_t flags = *(uint32_t *)arg;
return usbh_serial_tiocmset(serial, flags, 0);
} break;
case USBH_SERIAL_CMD_IOCMBIC: {
uint32_t flags = *(uint32_t *)arg;
return usbh_serial_tiocmset(serial, 0, flags);
} break;
case USBH_SERIAL_CMD_TIOCMSET: {
uint32_t flags = *(uint32_t *)arg;
uint32_t set = 0;
uint32_t clear = 0;
set |= (flags & USBH_SERIAL_TIOCM_DTR) ? USBH_SERIAL_TIOCM_DTR : 0;
set |= (flags & USBH_SERIAL_TIOCM_RTS) ? USBH_SERIAL_TIOCM_RTS : 0;
clear |= !(flags & USBH_SERIAL_TIOCM_DTR) ? USBH_SERIAL_TIOCM_DTR : 0;
clear |= !(flags & USBH_SERIAL_TIOCM_RTS) ? USBH_SERIAL_TIOCM_RTS : 0;
return usbh_serial_tiocmset(serial, set, clear);
} break;
case USBH_SERIAL_CMD_TIOCMGET: {
uint32_t *flags = (uint32_t *)arg;
int status;
if (serial && serial->driver && serial->driver->get_modem_status) {
status = serial->driver->get_modem_status(serial);
if (status < 0) {
return status;
}
} else {
return -USB_ERR_NOTSUPP;
}
*flags = status;
} break;
default:
break;
}
return -USB_ERR_NOTSUPP;
}
int usbh_serial_write(struct usbh_serial *serial, const void *buffer, uint32_t buflen)
{
int ret;
struct usbh_urb *urb;
if (!serial || !serial->hport || !serial->hport->connected || !serial->bulkout) {
return -USB_ERR_INVAL;
}
if (serial->ref_count == 0) {
return -USB_ERR_NODEV;
}
urb = &serial->bulkout_urb;
usbh_bulk_urb_fill(urb, serial->hport, serial->bulkout, (uint8_t *)buffer, serial->line_coding.dwDTERate ? MIN(buflen, serial->bulkout->wMaxPacketSize) : buflen, 0xffffffff, NULL, NULL);
ret = usbh_submit_urb(urb);
if (ret == 0) {
ret = urb->actual_length;
}
return ret;
}
int usbh_serial_read(struct usbh_serial *serial, void *buffer, uint32_t buflen)
{
int ret;
if (!serial || !serial->hport || !serial->hport->connected || !serial->bulkin || !serial->line_coding.dwDTERate) {
return -USB_ERR_INVAL;
}
if (serial->ref_count == 0) {
return -USB_ERR_NODEV;
}
if (serial->open_flags & USBH_SERIAL_O_NONBLOCK) {
return usbh_serial_ringbuffer_read(&serial->rx_rb, buffer, buflen);
} else {
if (usbh_serial_ringbuffer_get_used(&serial->rx_rb) == 0) {
ret = usb_osal_sem_take(serial->rx_complete_sem, serial->rx_timeout_ms == 0 ? USB_OSAL_WAITING_FOREVER : serial->rx_timeout_ms);
if (ret < 0) {
return ret;
}
if (serial->rx_errorcode < 0) {
return serial->rx_errorcode;
}
}
return usbh_serial_ringbuffer_read(&serial->rx_rb, buffer, buflen);
}
}
int usbh_serial_cdc_write_async(struct usbh_serial *serial, uint8_t *buffer, uint32_t buflen, usbh_complete_callback_t complete, void *arg)
{
struct usbh_urb *urb;
if (!serial || !serial->hport || !serial->hport->connected || !serial->bulkout || !complete || serial->line_coding.dwDTERate) {
return -USB_ERR_INVAL;
}
if (serial->ref_count > 0) {
return -USB_ERR_NODEV;
}
urb = &serial->bulkout_urb;
usbh_bulk_urb_fill(urb, serial->hport, serial->bulkout, buffer, buflen,
0, complete, serial);
return usbh_submit_urb(urb);
}
int usbh_serial_cdc_read_async(struct usbh_serial *serial, uint8_t *buffer, uint32_t buflen, usbh_complete_callback_t complete, void *arg)
{
struct usbh_urb *urb;
if (!serial || !serial->hport || !serial->hport->connected || !serial->bulkin || !complete || serial->line_coding.dwDTERate) {
return -USB_ERR_INVAL;
}
if (serial->ref_count > 0) {
return -USB_ERR_NODEV;
}
if (buflen % serial->bulkin->wMaxPacketSize) {
return -USB_ERR_INVAL;
}
urb = &serial->bulkin_urb;
usbh_bulk_urb_fill(urb, serial->hport, serial->bulkin, buffer, MIN(buflen, serial->bulkin->wMaxPacketSize),
0, complete, serial);
return usbh_submit_urb(urb);
}
void usbh_serial_help(void)
{
USB_LOG_RAW("USB host serial test\r\n"
"Usage: usbh_serial <ttypath> [options]...\r\n"
"\r\n"
"-b <baud> set serial baudrate\r\n"
"-t <dtr> <rts> set rts and dtr\r\n"
"-w string write string\r\n"
"-r read data and dump\r\n"
"-x close serial device\r\n"
"\r\n");
}
static USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_serial_testbuffer[512];
int usbh_serial(int argc, char **argv)
{
static struct usbh_serial *serial;
int ret;
if (argc < 3) {
usbh_serial_help();
return 0;
}
if (!serial) {
serial = usbh_serial_open(argv[1], USBH_SERIAL_O_RDWR | USBH_SERIAL_O_NONBLOCK);
if (!serial) {
USB_LOG_ERR("Fail to open serial device: %s\r\n", argv[1]);
return -USB_ERR_INVAL;
}
}
if (strncmp(argv[2], "-b", 2) == 0 && argc >= 4) {
struct usbh_serial_termios termios;
memset(&termios, 0, sizeof(termios));
termios.baudrate = atoi(argv[3]);
termios.stopbits = 0;
termios.parity = 0;
termios.databits = 8;
termios.rtscts = false;
termios.rx_timeout = 0;
usbh_serial_control(serial, USBH_SERIAL_CMD_SET_ATTR, &termios);
} else if (strncmp(argv[2], "-t", 2) == 0 && argc >= 5) {
uint32_t flags;
flags = atoi(argv[3]) ? USBH_SERIAL_TIOCM_DTR : 0;
flags |= atoi(argv[4]) ? USBH_SERIAL_TIOCM_RTS : 0;
usbh_serial_control(serial, USBH_SERIAL_CMD_TIOCMSET, &flags);
USB_LOG_INFO("Set DTR: %d, RTS: %d\r\n", atoi(argv[3]), atoi(argv[4]));
} else if (strncmp(argv[2], "-w", 2) == 0 && argc >= 4) {
memcpy(g_serial_testbuffer, argv[3], MIN(strlen(argv[3]), sizeof(g_serial_testbuffer)));
uint32_t len = snprintf((char *)g_serial_testbuffer, sizeof(g_serial_testbuffer), "%s\r\n", argv[3]);
ret = usbh_serial_write(serial, g_serial_testbuffer, len);
if (ret >= 0) {
USB_LOG_INFO("Write %d bytes\r\n", ret);
} else {
USB_LOG_ERR("Write failed: %d\r\n", ret);
}
} else if (strncmp(argv[2], "-r", 2) == 0) {
ret = usbh_serial_read(serial, g_serial_testbuffer, sizeof(g_serial_testbuffer));
if (ret >= 0) {
usb_hexdump(g_serial_testbuffer, ret);
USB_LOG_INFO("Read %d bytes\r\n", ret);
} else {
USB_LOG_ERR("Read failed: %d\r\n", ret);
}
} else if (strncmp(argv[2], "-x", 2) == 0) {
usbh_serial_close(serial);
serial = NULL;
} else {
usbh_serial_help();
}
return 0;
}
__WEAK void usbh_serial_run(struct usbh_serial *serial)
{
(void)serial;
}
__WEAK void usbh_serial_stop(struct usbh_serial *serial)
{
(void)serial;
}
static int usbh_cdc_data_connect(struct usbh_hubport *hport, uint8_t intf)
{
(void)hport;
(void)intf;
return 0;
}
static int usbh_cdc_data_disconnect(struct usbh_hubport *hport, uint8_t intf)
{
(void)hport;
(void)intf;
return 0;
}
const struct usbh_class_driver cdc_data_class_driver = {
.driver_name = "cdc_data",
.connect = usbh_cdc_data_connect,
.disconnect = usbh_cdc_data_disconnect
};
CLASS_INFO_DEFINE const struct usbh_class_info cdc_data_class_info = {
.match_flags = USB_CLASS_MATCH_INTF_CLASS,
.bInterfaceClass = USB_DEVICE_CLASS_CDC_DATA,
.bInterfaceSubClass = 0x00,
.bInterfaceProtocol = 0x00,
.id_table = NULL,
.class_driver = &cdc_data_class_driver
};

179
class/serial/usbh_serial.h Normal file
View File

@@ -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 */

View File

@@ -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
};

View File

@@ -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 */

View File

@@ -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
};

View File

@@ -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 */

View File

@@ -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
};

View File

@@ -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 */

View File

@@ -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
};

View File

@@ -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 */

View File

@@ -209,6 +209,23 @@
19, 18, 17, 16, 15, 14, 13, 12, 11, 10, \ 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, \
9, 8, 7, 6, 5, 4, 3, 2, 1, 0 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_MEM_ALIGNX __attribute__((aligned(CONFIG_USB_ALIGN_SIZE)))
#define USB_ALIGN_UP(size, align) (((size) + (align)-1) & ~((align)-1)) #define USB_ALIGN_UP(size, align) (((size) + (align)-1) & ~((align)-1))

View File

@@ -4,20 +4,20 @@
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
#include "usbh_core.h" #include "usbh_core.h"
#include "usbh_cdc_acm.h" #include "usbh_serial.h"
#include "usbh_hid.h" #include "usbh_hid.h"
#include "usbh_msc.h" #include "usbh_msc.h"
#include "usbh_video.h" #include "usbh_video.h"
#include "usbh_audio.h" #include "usbh_audio.h"
#ifndef CONFIG_TEST_USBH_CDC_ACM #ifndef CONFIG_TEST_USBH_SERIAL
#define CONFIG_TEST_USBH_CDC_ACM 1 #define CONFIG_TEST_USBH_SERIAL 1
#endif #endif
#ifndef TEST_USBH_CDC_SPEED #ifndef TEST_USBH_CDC_SPEED
#define TEST_USBH_CDC_SPEED 0 #define TEST_USBH_CDC_SPEED 0
#endif #endif
#ifndef CONFIG_TEST_USBH_HID #ifndef CONFIG_TEST_USBH_HID
#define CONFIG_TEST_USBH_HID 1 #define CONFIG_TEST_USBH_HID 0
#endif #endif
#ifndef CONFIG_TEST_USBH_MSC #ifndef CONFIG_TEST_USBH_MSC
#define CONFIG_TEST_USBH_MSC 1 #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 #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 #endif
#if CONFIG_TEST_USBH_CDC_ACM #if CONFIG_TEST_USBH_SERIAL
USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t cdc_buffer[4096]; #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 #if TEST_USBH_CDC_SPEED
#define TEST_LEN (16 * 1024) #define TEST_LEN (16 * 1024)
#define TEST_COUNT (10240) #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 #endif
void usbh_cdc_acm_callback(void *arg, int nbytes) static void usbh_serial_thread(CONFIG_USB_OSAL_THREAD_SET_ARGV)
{
//struct usbh_cdc_acm *cdc_acm_class = (struct usbh_cdc_acm *)arg;
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)
{ {
int ret; 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 */ /* test with only one buffer, if you have more cdc acm class, modify by yourself */
#if TEST_USBH_CDC_SPEED #if TEST_USBH_CDC_SPEED
const uint32_t test_len[] = { 512, 1 * 1024, 2 * 1024, 4 * 1024, 8 * 1024, 16 * 1024 }; 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++) { for (uint8_t j = 0; j < 6; j++) {
uint32_t start_time = (uint32_t)xTaskGetTickCount(); uint32_t start_time = (uint32_t)xTaskGetTickCount();
for (uint32_t i = 0; i < TEST_COUNT; i++) { 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); usbh_serial_write(serialize, serial_speed_buffer, test_len[j]);
ret = usbh_submit_urb(&cdc_acm_class->bulkout_urb);
if (ret < 0) { if (ret < 0) {
USB_LOG_RAW("bulk out error,ret:%d\r\n", ret); USB_LOG_RAW("bulk out error,ret:%d\r\n", ret);
while (1) { 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)); 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 #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 */ serial_tx_bytes = 0;
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); while (1) {
ret = usbh_submit_urb(&cdc_acm_class->bulkout_urb); /* for common, we use timeout with 0xffffffff, this is just a test */
if (ret < 0) { ret = usbh_serial_write(serial, serial_tx_buffer, sizeof(serial_tx_buffer));
USB_LOG_RAW("bulk out error,ret:%d\r\n", ret); if (ret < 0) {
goto delete; USB_LOG_RAW("serial write error, ret:%d\r\n", ret);
} else { goto delete_with_close;
USB_LOG_RAW("send over:%d\r\n", (unsigned int)cdc_acm_class->bulkout_urb.actual_length); } 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 */ volatile uint32_t wait_timeout = 0;
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); serial_rx_bytes = 0;
ret = usbh_submit_urb(&cdc_acm_class->bulkin_urb); while (1) {
if (ret < 0) { ret = usbh_serial_read(serial, &serial_rx_data[serial_rx_bytes], SERIAL_TEST_LEN);
USB_LOG_RAW("bulk in error,ret:%d\r\n", ret); if (ret < 0) {
goto delete; USB_LOG_RAW("serial read error, ret:%d\r\n", ret);
} else { 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 // clang-format off
delete_with_close:
usbh_serial_close(serial);
delete: delete:
usb_osal_thread_delete(NULL); usb_osal_thread_delete(NULL);
// clang-format on // clang-format on
@@ -140,7 +193,6 @@ static void usbh_hid_thread(CONFIG_USB_OSAL_THREAD_SET_ARGV)
{ {
int ret; int ret;
struct usbh_hid *hid_class = (struct usbh_hid *)CONFIG_USB_OSAL_THREAD_GET_ARGV; 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 */ /* 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 #if TEST_USBH_MSC_FATFS_SPEED
#define WRITE_SIZE_MB (128UL) #define WRITE_SIZE_MB (128UL)
#define WRITE_SIZE (1024UL * 1024UL * WRITE_SIZE_MB) #define WRITE_SIZE (1024UL * 1024UL * WRITE_SIZE_MB)
#define BUF_SIZE (1024UL * 128UL) #define BUF_SIZE (1024UL * 128UL)
USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t read_write_buffer[BUF_SIZE]; USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t read_write_buffer[BUF_SIZE];
#else #else
USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t read_write_buffer[25 * 100]; 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 write_size = WRITE_SIZE;
uint32_t start_time = (uint32_t)xTaskGetTickCount(); uint32_t start_time = (uint32_t)xTaskGetTickCount();
while (write_size > 0) { 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) { if (res_sd != FR_OK) {
USB_LOG_RAW("Write file failed, cause: %s\n", res_sd); USB_LOG_RAW("Write file failed, cause: %s\n", res_sd);
goto unmount; goto unmount;
@@ -260,7 +312,7 @@ int usb_msc_fatfs_test()
uint32_t write_size = WRITE_SIZE; uint32_t write_size = WRITE_SIZE;
uint32_t start_time = (uint32_t)xTaskGetTickCount(); uint32_t start_time = (uint32_t)xTaskGetTickCount();
while (write_size > 0) { 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) { if (res_sd != FR_OK) {
USB_LOG_RAW("Read file failed, cause: %s\n", res_sd); USB_LOG_RAW("Read file failed, cause: %s\n", res_sd);
goto unmount; goto unmount;
@@ -326,14 +378,19 @@ delete:
} }
#endif #endif
#if CONFIG_TEST_USBH_CDC_ACM #if CONFIG_TEST_USBH_SERIAL
void usbh_cdc_acm_run(struct usbh_cdc_acm *cdc_acm_class) 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 #endif

View File

@@ -154,14 +154,115 @@ lsusb
int lsusb(int argc, char **argv); 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 HID
----------------- -----------------
MSC MSC
----------------- -----------------
RNDIS NETWORK
----------------- -----------------

View File

@@ -1,4 +1,96 @@
usbh_serial usbh_serial
=============== ===============
当前仅支持 rt-thread device 框架,包括 cdc acm, ftdi, cp210x, ch34x, pl2303, 具体使用方式参考 rt-thread device api 即可。 Serial 框架当前支持 cdc acm, ftdi, cp210x, ch34x, pl2303gsm 驱动。当前支持两种使用方式,
一种是使用源生 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 设备需要使用第一种。

View File

@@ -158,7 +158,7 @@
#define CONFIG_USBHOST_MAX_INTF_ALTSETTINGS 2 #define CONFIG_USBHOST_MAX_INTF_ALTSETTINGS 2
#define CONFIG_USBHOST_MAX_ENDPOINTS 4 #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_HID_CLASS 4
#define CONFIG_USBHOST_MAX_MSC_CLASS 2 #define CONFIG_USBHOST_MAX_MSC_CLASS 2
#define CONFIG_USBHOST_MAX_AUDIO_CLASS 1 #define CONFIG_USBHOST_MAX_AUDIO_CLASS 1
@@ -193,6 +193,10 @@
#define CONFIG_USBHOST_MSC_TIMEOUT 5000 #define CONFIG_USBHOST_MSC_TIMEOUT 5000
#endif #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, /* 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. * you can change to 2K ~ 16K and must be larger than TCP RX windows size in order to avoid being overflow.
*/ */

View File

@@ -44,4 +44,10 @@ int usbh_deinit(int argc, char **argv)
MSH_CMD_EXPORT(usbh_init, init usb host); MSH_CMD_EXPORT(usbh_init, init usb host);
MSH_CMD_EXPORT(usbh_deinit, deinit usb host); MSH_CMD_EXPORT(usbh_deinit, deinit usb host);
MSH_CMD_EXPORT(lsusb, ls usb devices); 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 #endif

View File

@@ -0,0 +1,209 @@
/*
* Copyright (c) 2025, sakumisu
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <rtthread.h>
#include <rtdevice.h>
#include "usbh_core.h"
#include "usbh_serial.h"
static rt_err_t rt_usbh_serial_open(struct rt_device *dev, rt_uint16_t oflag)
{
struct usbh_serial *serial;
RT_ASSERT(dev != RT_NULL && dev->user_data != RT_NULL);
serial = (struct usbh_serial *)dev->user_data;
serial = usbh_serial_open(serial->hport->config.intf[serial->intf].devname, USBH_SERIAL_O_RDWR | USBH_SERIAL_O_NONBLOCK);
if (serial == RT_NULL) {
USB_LOG_ERR("serial open failed\n");
return -RT_ERROR;
}
struct usbh_serial_termios termios;
memset(&termios, 0, sizeof(termios));
termios.baudrate = 115200;
termios.stopbits = 0;
termios.parity = 0;
termios.databits = 8;
termios.rtscts = false;
termios.rx_timeout = 0;
usbh_serial_control(serial, USBH_SERIAL_CMD_SET_ATTR, &termios);
return RT_EOK;
}
static rt_err_t rt_usbh_serial_close(struct rt_device *dev)
{
struct usbh_serial *serial;
RT_ASSERT(dev != RT_NULL && dev->user_data != RT_NULL);
serial = (struct usbh_serial *)dev->user_data;
usbh_serial_close(serial);
return RT_EOK;
}
static rt_ssize_t rt_usbh_serial_read(struct rt_device *dev,
rt_off_t pos,
void *buffer,
rt_size_t size)
{
struct usbh_serial *serial;
RT_ASSERT(dev != RT_NULL && dev->user_data != RT_NULL);
serial = (struct usbh_serial *)dev->user_data;
return usbh_serial_read(serial, buffer, size);
}
static rt_ssize_t rt_usbh_serial_write(struct rt_device *dev,
rt_off_t pos,
const void *buffer,
rt_size_t size)
{
struct usbh_serial *serial;
int ret = 0;
rt_uint8_t *align_buf;
RT_ASSERT(dev != RT_NULL && dev->user_data != RT_NULL);
serial = (struct usbh_serial *)dev->user_data;
align_buf = (rt_uint8_t *)buffer;
if ((uint32_t)buffer & (CONFIG_USB_ALIGN_SIZE - 1)) {
align_buf = rt_malloc_align(USB_ALIGN_UP(size, CONFIG_USB_ALIGN_SIZE), CONFIG_USB_ALIGN_SIZE);
if (!align_buf) {
USB_LOG_ERR("serial get align buf failed\n");
return 0;
}
usb_memcpy(align_buf, buffer, size);
}
ret = usbh_serial_write(serial, align_buf, size);
if ((uint32_t)buffer & (CONFIG_USB_ALIGN_SIZE - 1)) {
rt_free_align(align_buf);
}
return ret;
}
static rt_err_t rt_usbh_serial_control(struct rt_device *dev,
int cmd,
void *args)
{
struct usbh_serial *serial;
struct serial_configure *config;
int ret = -RT_EINVAL;
RT_ASSERT(dev != RT_NULL && dev->user_data != RT_NULL);
serial = (struct usbh_serial *)dev->user_data;
switch (cmd) {
case RT_DEVICE_CTRL_CONFIG: {
config = (struct serial_configure *)args;
struct usbh_serial_termios termios;
memset(&termios, 0, sizeof(termios));
termios.baudrate = config->baud_rate;
termios.stopbits = 0;
termios.parity = config->parity;
termios.databits = config->data_bits;
termios.rtscts = false;
termios.rx_timeout = 0;
usbh_serial_control(serial, USBH_SERIAL_CMD_SET_ATTR, &termios);
} break;
default:
break;
}
return ret;
}
#ifdef RT_USING_DEVICE_OPS
const static struct rt_device_ops usbh_serial_ops = {
NULL,
rt_usbh_serial_open,
rt_usbh_serial_close,
rt_usbh_serial_read,
rt_usbh_serial_write,
rt_usbh_serial_control
};
#endif
rt_err_t usbh_serial_register(struct usbh_serial *serial)
{
rt_err_t ret;
struct rt_device *device;
RT_ASSERT(serial != RT_NULL);
device = rt_malloc(sizeof(struct rt_device));
if (device == RT_NULL) {
USB_LOG_ERR("serial device malloc failed\n");
return -RT_ENOMEM;
}
memset(device, 0, sizeof(struct rt_device));
device->type = RT_Device_Class_Char;
device->rx_indicate = RT_NULL;
device->tx_complete = RT_NULL;
#ifdef RT_USING_DEVICE_OPS
device->ops = &usbh_serial_ops;
#else
device->init = NULL;
device->open = rt_usbh_serial_open;
device->close = rt_usbh_serial_close;
device->read = rt_usbh_serial_read;
device->write = rt_usbh_serial_write;
device->control = rt_usbh_serial_control;
#endif
device->user_data = serial;
serial->user_data = device;
/* register a character device */
ret = rt_device_register(device, serial->hport->config.intf[serial->intf].devname, RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX | RT_DEVICE_FLAG_REMOVABLE);
#ifdef RT_USING_POSIX_DEVIO
/* set fops */
device->fops = &usbh_serial_fops;
#endif
return ret;
}
void usbh_serial_unregister(struct usbh_serial *serial)
{
struct rt_device *device;
RT_ASSERT(serial != NULL && serial->user_data != NULL);
device = (struct rt_device *)serial->user_data;
rt_device_unregister(device);
rt_free(device);
}
void usbh_serial_run(struct usbh_serial *serial)
{
usbh_serial_register(serial);
}
void usbh_serial_stop(struct usbh_serial *serial)
{
usbh_serial_unregister(serial);
}

View File

@@ -1,899 +0,0 @@
/*
* Copyright (c) 2025, sakumisu
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <rtthread.h>
#include <rtdevice.h>
#include "usbh_core.h"
#include "usbh_cdc_acm.h"
#include "usbh_ftdi.h"
#include "usbh_cp210x.h"
#include "usbh_ch34x.h"
#include "usbh_pl2303.h"
#define DEV_FORMAT_VENDOR "ttyUSB%d"
#define DEV_FORMAT_CDC_ACM "ttyACM%d"
#define USBH_RX_MAX_SIZE 2048
#ifndef CONFIG_USBHOST_MAX_VENDOR_SERIAL_CLASS
#define CONFIG_USBHOST_MAX_VENDOR_SERIAL_CLASS (4)
#endif
#ifndef CONFIG_USBHOST_SERIAL_RX_BUFSIZE
#define CONFIG_USBHOST_SERIAL_RX_BUFSIZE (USBH_RX_MAX_SIZE * 2)
#endif
enum usbh_serial_type {
USBH_SERIAL_TYPE_CDC_ACM = 0,
USBH_SERIAL_TYPE_FTDI,
USBH_SERIAL_TYPE_CP210X,
USBH_SERIAL_TYPE_CH34X,
USBH_SERIAL_TYPE_PL2303,
};
struct usbh_serial {
struct rt_device parent;
enum usbh_serial_type type;
uint8_t minor;
char name[CONFIG_USBHOST_DEV_NAMELEN];
struct rt_ringbuffer rx_rb;
rt_uint8_t rx_rb_buffer[CONFIG_USBHOST_SERIAL_RX_BUFSIZE];
};
static uint32_t g_devinuse_vendor = 0;
static uint32_t g_devinuse_cdc_acm = 0;
static USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_usbh_serial_vendor_rx_buf[CONFIG_USBHOST_MAX_VENDOR_SERIAL_CLASS][USB_ALIGN_UP(USBH_RX_MAX_SIZE, CONFIG_USB_ALIGN_SIZE)];
static USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_usbh_serial_cdc_acm_rx_buf[CONFIG_USBHOST_MAX_CDC_ACM_CLASS][USB_ALIGN_UP(USBH_RX_MAX_SIZE, CONFIG_USB_ALIGN_SIZE)];
static struct usbh_serial *usbh_serial_alloc(uint8_t type)
{
uint8_t devno;
struct usbh_serial *serial;
for (devno = 0; devno < CONFIG_USBHOST_MAX_VENDOR_SERIAL_CLASS; devno++) {
if ((g_devinuse_vendor & (1U << devno)) == 0) {
g_devinuse_vendor |= (1U << devno);
serial = rt_malloc(sizeof(struct usbh_serial));
memset(serial, 0, sizeof(struct usbh_serial));
serial->type = type;
serial->minor = devno;
snprintf(serial->name, CONFIG_USBHOST_DEV_NAMELEN, DEV_FORMAT_VENDOR, serial->minor);
return serial;
}
}
return NULL;
}
static void usbh_serial_free(struct usbh_serial *serial)
{
uint8_t devno = serial->minor;
if (devno < 32) {
g_devinuse_vendor &= ~(1U << devno);
}
memset(serial, 0, sizeof(struct usbh_serial));
rt_free(serial);
}
static struct usbh_serial *usbh_serial_cdc_acm_alloc(uint8_t type)
{
uint8_t devno;
struct usbh_serial *serial;
for (devno = 0; devno < CONFIG_USBHOST_MAX_CDC_ACM_CLASS; devno++) {
if ((g_devinuse_cdc_acm & (1U << devno)) == 0) {
g_devinuse_cdc_acm |= (1U << devno);
serial = rt_malloc(sizeof(struct usbh_serial));
memset(serial, 0, sizeof(struct usbh_serial));
serial->type = type;
serial->minor = devno;
snprintf(serial->name, CONFIG_USBHOST_DEV_NAMELEN, DEV_FORMAT_CDC_ACM, serial->minor);
return serial;
}
}
return NULL;
}
static void usbh_serial_cdc_acm_free(struct usbh_serial *serial)
{
uint8_t devno = serial->minor;
if (devno < 32) {
g_devinuse_cdc_acm &= ~(1U << devno);
}
memset(serial, 0, sizeof(struct usbh_serial));
rt_free(serial);
}
static rt_err_t usbh_serial_open(struct rt_device *dev, rt_uint16_t oflag)
{
struct usbh_serial *serial;
RT_ASSERT(dev != RT_NULL);
serial = (struct usbh_serial *)dev;
switch (serial->type) {
case USBH_SERIAL_TYPE_CDC_ACM:
break;
case USBH_SERIAL_TYPE_FTDI:
break;
case USBH_SERIAL_TYPE_CP210X:
break;
case USBH_SERIAL_TYPE_CH34X:
break;
case USBH_SERIAL_TYPE_PL2303:
break;
default:
break;
}
return RT_EOK;
}
static rt_err_t usbh_serial_close(struct rt_device *dev)
{
struct usbh_serial *serial;
RT_ASSERT(dev != RT_NULL);
serial = (struct usbh_serial *)dev;
switch (serial->type) {
case USBH_SERIAL_TYPE_CDC_ACM:
break;
case USBH_SERIAL_TYPE_FTDI:
break;
case USBH_SERIAL_TYPE_CP210X:
break;
case USBH_SERIAL_TYPE_CH34X:
break;
case USBH_SERIAL_TYPE_PL2303:
break;
default:
break;
}
return RT_EOK;
}
static rt_ssize_t usbh_serial_read(struct rt_device *dev,
rt_off_t pos,
void *buffer,
rt_size_t size)
{
struct usbh_serial *serial;
RT_ASSERT(dev != RT_NULL);
serial = (struct usbh_serial *)dev;
return rt_ringbuffer_get(&serial->rx_rb, (rt_uint8_t *)buffer, size);
}
static rt_ssize_t usbh_serial_write(struct rt_device *dev,
rt_off_t pos,
const void *buffer,
rt_size_t size)
{
struct usbh_serial *serial;
int ret = 0;
rt_uint8_t *align_buf;
RT_ASSERT(dev != RT_NULL);
serial = (struct usbh_serial *)dev;
align_buf = (rt_uint8_t *)buffer;
if ((uint32_t)buffer & (CONFIG_USB_ALIGN_SIZE - 1)) {
align_buf = rt_malloc_align(USB_ALIGN_UP(size, CONFIG_USB_ALIGN_SIZE), CONFIG_USB_ALIGN_SIZE);
if (!align_buf) {
USB_LOG_ERR("serial get align buf failed\n");
return 0;
}
usb_memcpy(align_buf, buffer, size);
}
switch (serial->type) {
#if defined(PKG_CHERRYUSB_HOST_CDC_ACM) || defined(RT_CHERRYUSB_HOST_CDC_ACM)
case USBH_SERIAL_TYPE_CDC_ACM:
ret = usbh_cdc_acm_bulk_out_transfer((struct usbh_cdc_acm *)dev->user_data, (uint8_t *)align_buf, size, RT_WAITING_FOREVER);
if (ret < 0) {
USB_LOG_ERR("usbh_cdc_acm_bulk_out_transfer failed: %d\n", ret);
ret = 0;
}
break;
#endif
#if defined(PKG_CHERRYUSB_HOST_FTDI) || defined(RT_CHERRYUSB_HOST_FTDI)
case USBH_SERIAL_TYPE_FTDI:
ret = usbh_ftdi_bulk_out_transfer((struct usbh_ftdi *)dev->user_data, (uint8_t *)align_buf, size, RT_WAITING_FOREVER);
if (ret < 0) {
USB_LOG_ERR("usbh_ftdi_bulk_out_transfer failed: %d\n", ret);
ret = 0;
}
break;
#endif
#if defined(PKG_CHERRYUSB_HOST_CH34X) || defined(RT_CHERRYUSB_HOST_CH34X)
case USBH_SERIAL_TYPE_CH34X:
ret = usbh_ch34x_bulk_out_transfer((struct usbh_ch34x *)dev->user_data, (uint8_t *)align_buf, size, RT_WAITING_FOREVER);
if (ret < 0) {
USB_LOG_ERR("usbh_ch34x_bulk_out_transfer failed: %d\n", ret);
ret = 0;
}
break;
#endif
#if defined(PKG_CHERRYUSB_HOST_PL2303) || defined(RT_CHERRYUSB_HOST_PL2303)
case USBH_SERIAL_TYPE_PL2303:
ret = usbh_pl2303_bulk_out_transfer((struct usbh_pl2303 *)dev->user_data, (uint8_t *)align_buf, size, RT_WAITING_FOREVER);
if (ret < 0) {
USB_LOG_ERR("usbh_pl2303_bulk_out_transfer failed: %d\n", ret);
ret = 0;
}
break;
#endif
default:
break;
}
if ((uint32_t)buffer & (CONFIG_USB_ALIGN_SIZE - 1)) {
rt_free_align(align_buf);
}
return ret;
}
static rt_err_t usbh_serial_control(struct rt_device *dev,
int cmd,
void *args)
{
struct usbh_serial *serial;
struct serial_configure *config;
struct cdc_line_coding line_coding;
int ret = -RT_EINVAL;
RT_ASSERT(dev != RT_NULL);
serial = (struct usbh_serial *)dev;
switch (serial->type) {
#if defined(PKG_CHERRYUSB_HOST_CDC_ACM) || defined(RT_CHERRYUSB_HOST_CDC_ACM)
case USBH_SERIAL_TYPE_CDC_ACM:
if (cmd == RT_DEVICE_CTRL_CONFIG) {
struct usbh_cdc_acm *cdc_acm_class;
cdc_acm_class = (struct usbh_cdc_acm *)dev->user_data;
config = (struct serial_configure *)args;
line_coding.dwDTERate = config->baud_rate;
line_coding.bDataBits = config->data_bits;
line_coding.bCharFormat = 0; // STOP_BITS_1
line_coding.bParityType = config->parity;
usbh_cdc_acm_set_line_coding(cdc_acm_class, &line_coding);
}
ret = RT_EOK;
break;
#endif
#if defined(PKG_CHERRYUSB_HOST_FTDI) || defined(RT_CHERRYUSB_HOST_FTDI)
case USBH_SERIAL_TYPE_FTDI:
if (cmd == RT_DEVICE_CTRL_CONFIG) {
struct usbh_ftdi *ftdi_class;
ftdi_class = (struct usbh_ftdi *)dev->user_data;
config = (struct serial_configure *)args;
line_coding.dwDTERate = config->baud_rate;
line_coding.bDataBits = config->data_bits;
line_coding.bCharFormat = 0; // STOP_BITS_1
line_coding.bParityType = config->parity;
usbh_ftdi_set_line_coding(ftdi_class, &line_coding);
}
ret = RT_EOK;
break;
#endif
#if defined(PKG_CHERRYUSB_HOST_CP210X) || defined(RT_CHERRYUSB_HOST_CP210X)
case USBH_SERIAL_TYPE_CP210X:
if (cmd == RT_DEVICE_CTRL_CONFIG) {
struct usbh_cp210x *cp210x_class;
cp210x_class = (struct usbh_cp210x *)dev->user_data;
config = (struct serial_configure *)args;
line_coding.dwDTERate = config->baud_rate;
line_coding.bDataBits = config->data_bits;
line_coding.bCharFormat = 0; // STOP_BITS_1
line_coding.bParityType = config->parity;
usbh_cp210x_set_line_coding(cp210x_class, &line_coding);
}
ret = RT_EOK;
break;
#endif
#if defined(PKG_CHERRYUSB_HOST_CH34X) || defined(RT_CHERRYUSB_HOST_CH34X)
case USBH_SERIAL_TYPE_CH34X:
if (cmd == RT_DEVICE_CTRL_CONFIG) {
struct usbh_ch34x *ch34x_class;
ch34x_class = (struct usbh_ch34x *)dev->user_data;
config = (struct serial_configure *)args;
line_coding.dwDTERate = config->baud_rate;
line_coding.bDataBits = config->data_bits;
line_coding.bCharFormat = 0; // STOP_BITS_1
line_coding.bParityType = config->parity;
usbh_ch34x_set_line_coding(ch34x_class, &line_coding);
}
ret = RT_EOK;
break;
#endif
#if defined(PKG_CHERRYUSB_HOST_PL2303) || defined(RT_CHERRYUSB_HOST_PL2303)
case USBH_SERIAL_TYPE_PL2303:
if (cmd == RT_DEVICE_CTRL_CONFIG) {
struct usbh_pl2303 *pl2303_class;
pl2303_class = (struct usbh_pl2303 *)dev->user_data;
config = (struct serial_configure *)args;
line_coding.dwDTERate = config->baud_rate;
line_coding.bDataBits = config->data_bits;
line_coding.bCharFormat = 0; // STOP_BITS_1
line_coding.bParityType = config->parity;
usbh_pl2303_set_line_coding(pl2303_class, &line_coding);
}
ret = RT_EOK;
break;
#endif
default:
break;
}
return ret;
}
#ifdef RT_USING_DEVICE_OPS
const static struct rt_device_ops usbh_serial_ops = {
NULL,
usbh_serial_open,
usbh_serial_close,
usbh_serial_read,
usbh_serial_write,
usbh_serial_control
};
#endif
#ifdef RT_USING_POSIX_DEVIO
#include <unistd.h>
#include <fcntl.h>
#include <poll.h>
#include <sys/ioctl.h>
#include <dfs_file.h>
#ifdef RT_USING_POSIX_TERMIOS
#include <termios.h>
#endif
static rt_err_t usbh_serial_fops_rx_ind(rt_device_t dev, rt_size_t size)
{
rt_wqueue_wakeup(&(dev->wait_queue), (void*)POLLIN);
return RT_EOK;
}
/* fops for serial */
static int usbh_serial_fops_open(struct dfs_file *fd)
{
rt_err_t ret = 0;
rt_uint16_t flags = 0;
rt_device_t device;
device = (rt_device_t)fd->vnode->data;
RT_ASSERT(device != RT_NULL);
switch (fd->flags & O_ACCMODE)
{
case O_RDONLY:
USB_LOG_DBG("fops open: O_RDONLY!");
flags = RT_DEVICE_FLAG_RDONLY;
break;
case O_WRONLY:
USB_LOG_DBG("fops open: O_WRONLY!");
flags = RT_DEVICE_FLAG_WRONLY;
break;
case O_RDWR:
USB_LOG_DBG("fops open: O_RDWR!");
flags = RT_DEVICE_FLAG_RDWR;
break;
default:
USB_LOG_ERR("fops open: unknown mode - %d!", fd->flags & O_ACCMODE);
break;
}
if ((fd->flags & O_ACCMODE) != O_WRONLY)
rt_device_set_rx_indicate(device, usbh_serial_fops_rx_ind);
ret = rt_device_open(device, flags);
if (ret == RT_EOK) return 0;
return ret;
}
static int usbh_serial_fops_close(struct dfs_file *fd)
{
rt_device_t device;
device = (rt_device_t)fd->vnode->data;
rt_device_set_rx_indicate(device, RT_NULL);
rt_device_close(device);
return 0;
}
static int usbh_serial_fops_ioctl(struct dfs_file *fd, int cmd, void *args)
{
rt_device_t device;
int flags = (int)(rt_base_t)args;
int mask = O_NONBLOCK | O_APPEND;
device = (rt_device_t)fd->vnode->data;
switch (cmd)
{
case FIONREAD:
break;
case FIONWRITE:
break;
case F_SETFL:
flags &= mask;
fd->flags &= ~mask;
fd->flags |= flags;
break;
}
return rt_device_control(device, cmd, args);
}
static int usbh_serial_fops_read(struct dfs_file *fd, void *buf, size_t count)
{
int size = 0;
rt_device_t device;
device = (rt_device_t)fd->vnode->data;
do
{
size = rt_device_read(device, -1, buf, count);
if (size <= 0)
{
if (fd->flags & O_NONBLOCK)
{
size = -EAGAIN;
break;
}
rt_wqueue_wait(&(device->wait_queue), 0, RT_WAITING_FOREVER);
}
}while (size <= 0);
return size;
}
static int usbh_serial_fops_write(struct dfs_file *fd, const void *buf, size_t count)
{
rt_device_t device;
device = (rt_device_t)fd->vnode->data;
return rt_device_write(device, -1, buf, count);
}
static int usbh_serial_fops_poll(struct dfs_file *fd, struct rt_pollreq *req)
{
int mask = 0;
int flags = 0;
rt_device_t device;
struct usbh_serial *serial;
device = (rt_device_t)fd->vnode->data;
RT_ASSERT(device != RT_NULL);
serial = (struct usbh_serial *)device;
/* only support POLLIN */
flags = fd->flags & O_ACCMODE;
if (flags == O_RDONLY || flags == O_RDWR)
{
rt_base_t level;
rt_poll_add(&(device->wait_queue), req);
level = rt_hw_interrupt_disable();
if (rt_ringbuffer_data_len(&serial->rx_rb))
mask |= POLLIN;
rt_hw_interrupt_enable(level);
}
// mask|=POLLOUT;
return mask;
}
const static struct dfs_file_ops usbh_serial_fops =
{
usbh_serial_fops_open,
usbh_serial_fops_close,
usbh_serial_fops_ioctl,
usbh_serial_fops_read,
usbh_serial_fops_write,
RT_NULL, /* flush */
RT_NULL, /* lseek */
RT_NULL, /* getdents */
usbh_serial_fops_poll,
};
#endif /* RT_USING_POSIX_DEVIO */
rt_err_t usbh_serial_register(struct usbh_serial *serial,
void *data)
{
rt_err_t ret;
struct rt_device *device;
RT_ASSERT(serial != RT_NULL);
device = &(serial->parent);
device->type = RT_Device_Class_Char;
device->rx_indicate = RT_NULL;
device->tx_complete = RT_NULL;
#ifdef RT_USING_DEVICE_OPS
device->ops = &usbh_serial_ops;
#else
device->init = NULL;
device->open = usbh_serial_open;
device->close = usbh_serial_close;
device->read = usbh_serial_read;
device->write = usbh_serial_write;
device->control = usbh_serial_control;
#endif
device->user_data = data;
/* register a character device */
ret = rt_device_register(device, serial->name, RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX | RT_DEVICE_FLAG_REMOVABLE);
#ifdef RT_USING_POSIX_DEVIO
/* set fops */
device->fops = &usbh_serial_fops;
#endif
rt_ringbuffer_init(&serial->rx_rb, serial->rx_rb_buffer, sizeof(serial->rx_rb_buffer));
return ret;
}
void usbh_serial_unregister(struct usbh_serial *serial)
{
RT_ASSERT(serial != NULL);
rt_device_unregister(&serial->parent);
if (serial->type == USBH_SERIAL_TYPE_CDC_ACM) {
usbh_serial_cdc_acm_free(serial);
} else {
usbh_serial_free(serial);
}
}
#if defined(PKG_CHERRYUSB_HOST_CDC_ACM) || defined(RT_CHERRYUSB_HOST_CDC_ACM)
void usbh_cdc_acm_callback(void *arg, int nbytes)
{
struct usbh_cdc_acm *cdc_acm_class = (struct usbh_cdc_acm *)arg;
struct usbh_serial *serial;
int ret;
struct usbh_urb *urb = &cdc_acm_class->bulkin_urb;
if (nbytes > 0) {
serial = (struct usbh_serial *)cdc_acm_class->user_data;
rt_ringbuffer_put(&serial->rx_rb, g_usbh_serial_cdc_acm_rx_buf[serial->minor], nbytes);
if (serial->parent.rx_indicate) {
serial->parent.rx_indicate(&serial->parent, nbytes);
}
usbh_bulk_urb_fill(urb, cdc_acm_class->hport, cdc_acm_class->bulkin, g_usbh_serial_cdc_acm_rx_buf[serial->minor], sizeof(g_usbh_serial_cdc_acm_rx_buf[serial->minor]), 0, usbh_cdc_acm_callback, cdc_acm_class);
ret = usbh_submit_urb(urb);
if (ret < 0) {
USB_LOG_ERR("usbh_submit_urb failed: %d\n", ret);
}
}
}
void usbh_cdc_acm_run(struct usbh_cdc_acm *cdc_acm_class)
{
struct usbh_serial *serial;
int ret;
struct usbh_urb *urb = &cdc_acm_class->bulkin_urb;
serial = usbh_serial_cdc_acm_alloc(USBH_SERIAL_TYPE_CDC_ACM);
cdc_acm_class->user_data = serial;
usbh_serial_register(serial, cdc_acm_class);
struct cdc_line_coding linecoding;
linecoding.dwDTERate = 115200;
linecoding.bDataBits = 8;
linecoding.bParityType = 0;
linecoding.bCharFormat = 0;
usbh_cdc_acm_set_line_coding(cdc_acm_class, &linecoding);
usbh_bulk_urb_fill(urb, cdc_acm_class->hport, cdc_acm_class->bulkin, g_usbh_serial_cdc_acm_rx_buf[serial->minor], sizeof(g_usbh_serial_cdc_acm_rx_buf[serial->minor]), 0, usbh_cdc_acm_callback, cdc_acm_class);
ret = usbh_submit_urb(urb);
if (ret < 0) {
USB_LOG_ERR("usbh_submit_urb failed: %d\n", ret);
usbh_serial_unregister(serial);
return;
}
}
void usbh_cdc_acm_stop(struct usbh_cdc_acm *cdc_acm_class)
{
struct usbh_serial *serial;
serial = (struct usbh_serial *)cdc_acm_class->user_data;
usbh_serial_unregister(serial);
}
#endif
#if defined(PKG_CHERRYUSB_HOST_FTDI) || defined(RT_CHERRYUSB_HOST_FTDI)
void usbh_ftdi_callback(void *arg, int nbytes)
{
struct usbh_ftdi *ftdi_class = (struct usbh_ftdi *)arg;
struct usbh_serial *serial;
int ret;
struct usbh_urb *urb = &ftdi_class->bulkin_urb;
if (nbytes >= 2) {
serial = (struct usbh_serial *)ftdi_class->user_data;
nbytes -= 2; // Skip the first two bytes (header)
rt_ringbuffer_put(&serial->rx_rb, &g_usbh_serial_vendor_rx_buf[serial->minor][2], nbytes);
if (serial->parent.rx_indicate && nbytes) {
serial->parent.rx_indicate(&serial->parent, nbytes);
}
usbh_bulk_urb_fill(urb, ftdi_class->hport, ftdi_class->bulkin, g_usbh_serial_vendor_rx_buf[serial->minor], sizeof(g_usbh_serial_vendor_rx_buf[serial->minor]), 0, usbh_ftdi_callback, ftdi_class);
ret = usbh_submit_urb(urb);
if (ret < 0) {
USB_LOG_ERR("usbh_submit_urb failed: %d\n", ret);
}
}
}
void usbh_ftdi_run(struct usbh_ftdi *ftdi_class)
{
struct usbh_serial *serial;
int ret;
struct usbh_urb *urb = &ftdi_class->bulkin_urb;
serial = usbh_serial_alloc(USBH_SERIAL_TYPE_FTDI);
ftdi_class->user_data = serial;
usbh_serial_register(serial, ftdi_class);
struct cdc_line_coding linecoding;
linecoding.dwDTERate = 115200;
linecoding.bDataBits = 8;
linecoding.bParityType = 0;
linecoding.bCharFormat = 0;
usbh_ftdi_set_line_coding(ftdi_class, &linecoding);
usbh_bulk_urb_fill(urb, ftdi_class->hport, ftdi_class->bulkin, g_usbh_serial_vendor_rx_buf[serial->minor], sizeof(g_usbh_serial_vendor_rx_buf[serial->minor]), 0, usbh_ftdi_callback, ftdi_class);
ret = usbh_submit_urb(urb);
if (ret < 0) {
USB_LOG_ERR("usbh_submit_urb failed: %d\n", ret);
usbh_serial_unregister(serial);
return;
}
}
void usbh_ftdi_stop(struct usbh_ftdi *ftdi_class)
{
struct usbh_serial *serial;
serial = (struct usbh_serial *)ftdi_class->user_data;
usbh_serial_unregister(serial);
}
#endif
#if defined(PKG_CHERRYUSB_HOST_CH34X) || defined(RT_CHERRYUSB_HOST_CH34X)
void usbh_ch34x_callback(void *arg, int nbytes)
{
struct usbh_ch34x *ch34x_class = (struct usbh_ch34x *)arg;
struct usbh_serial *serial;
int ret;
struct usbh_urb *urb = &ch34x_class->bulkin_urb;
if (nbytes > 0) {
serial = (struct usbh_serial *)ch34x_class->user_data;
rt_ringbuffer_put(&serial->rx_rb, g_usbh_serial_vendor_rx_buf[serial->minor], nbytes);
if (serial->parent.rx_indicate) {
serial->parent.rx_indicate(&serial->parent, nbytes);
}
usbh_bulk_urb_fill(urb, ch34x_class->hport, ch34x_class->bulkin, g_usbh_serial_vendor_rx_buf[serial->minor], sizeof(g_usbh_serial_vendor_rx_buf[serial->minor]), 0, usbh_ch34x_callback, ch34x_class);
ret = usbh_submit_urb(urb);
if (ret < 0) {
USB_LOG_ERR("usbh_submit_urb failed: %d\n", ret);
}
}
}
void usbh_ch34x_run(struct usbh_ch34x *ch34x_class)
{
struct usbh_serial *serial;
int ret;
struct usbh_urb *urb = &ch34x_class->bulkin_urb;
serial = usbh_serial_alloc(USBH_SERIAL_TYPE_CH34X);
ch34x_class->user_data = serial;
usbh_serial_register(serial, ch34x_class);
struct cdc_line_coding linecoding;
linecoding.dwDTERate = 115200;
linecoding.bDataBits = 8;
linecoding.bParityType = 0;
linecoding.bCharFormat = 0;
usbh_ch34x_set_line_coding(ch34x_class, &linecoding);
usbh_bulk_urb_fill(urb, ch34x_class->hport, ch34x_class->bulkin, g_usbh_serial_vendor_rx_buf[serial->minor], sizeof(g_usbh_serial_vendor_rx_buf[serial->minor]), 0, usbh_ch34x_callback, ch34x_class);
ret = usbh_submit_urb(urb);
if (ret < 0) {
USB_LOG_ERR("usbh_submit_urb failed: %d\n", ret);
usbh_serial_unregister(serial);
return;
}
}
void usbh_ch34x_stop(struct usbh_ch34x *ch34x_class)
{
struct usbh_serial *serial;
serial = (struct usbh_serial *)ch34x_class->user_data;
usbh_serial_unregister(serial);
}
#endif
#if defined(PKG_CHERRYUSB_HOST_CP210X) || defined(RT_CHERRYUSB_HOST_CP210X)
void usbh_cp210x_callback(void *arg, int nbytes)
{
struct usbh_cp210x *cp210x_class = (struct usbh_cp210x *)arg;
struct usbh_serial *serial;
int ret;
struct usbh_urb *urb = &cp210x_class->bulkin_urb;
if (nbytes > 0) {
serial = (struct usbh_serial *)cp210x_class->user_data;
rt_ringbuffer_put(&serial->rx_rb, g_usbh_serial_vendor_rx_buf[serial->minor], nbytes);
if (serial->parent.rx_indicate) {
serial->parent.rx_indicate(&serial->parent, nbytes);
}
usbh_bulk_urb_fill(urb, cp210x_class->hport, cp210x_class->bulkin, g_usbh_serial_vendor_rx_buf[serial->minor], sizeof(g_usbh_serial_vendor_rx_buf[serial->minor]), 0, usbh_cp210x_callback, cp210x_class);
ret = usbh_submit_urb(urb);
if (ret < 0) {
USB_LOG_ERR("usbh_submit_urb failed: %d\n", ret);
}
}
}
void usbh_cp210x_run(struct usbh_cp210x *cp210x_class)
{
struct usbh_serial *serial;
int ret;
struct usbh_urb *urb = &cp210x_class->bulkin_urb;
serial = usbh_serial_alloc(USBH_SERIAL_TYPE_CP210X);
cp210x_class->user_data = serial;
usbh_serial_register(serial, cp210x_class);
struct cdc_line_coding linecoding;
linecoding.dwDTERate = 115200;
linecoding.bDataBits = 8;
linecoding.bParityType = 0;
linecoding.bCharFormat = 0;
usbh_cp210x_set_line_coding(cp210x_class, &linecoding);
usbh_bulk_urb_fill(urb, cp210x_class->hport, cp210x_class->bulkin, g_usbh_serial_vendor_rx_buf[serial->minor], sizeof(g_usbh_serial_vendor_rx_buf[serial->minor]), 0, usbh_cp210x_callback, cp210x_class);
ret = usbh_submit_urb(urb);
if (ret < 0) {
USB_LOG_ERR("usbh_submit_urb failed: %d\n", ret);
usbh_serial_unregister(serial);
return;
}
}
void usbh_cp210x_stop(struct usbh_cp210x *cp210x_class)
{
struct usbh_serial *serial;
serial = (struct usbh_serial *)cp210x_class->user_data;
usbh_serial_unregister(serial);
}
#endif
#if defined(PKG_CHERRYUSB_HOST_PL2303) || defined(RT_CHERRYUSB_HOST_PL2303)
void usbh_pl2303_callback(void *arg, int nbytes)
{
struct usbh_pl2303 *pl2303_class = (struct usbh_pl2303 *)arg;
struct usbh_serial *serial;
int ret;
struct usbh_urb *urb = &pl2303_class->bulkin_urb;
if (nbytes > 0) {
serial = (struct usbh_serial *)pl2303_class->user_data;
rt_ringbuffer_put(&serial->rx_rb, g_usbh_serial_vendor_rx_buf[serial->minor], nbytes);
if (serial->parent.rx_indicate) {
serial->parent.rx_indicate(&serial->parent, nbytes);
}
usbh_bulk_urb_fill(urb, pl2303_class->hport, pl2303_class->bulkin, g_usbh_serial_vendor_rx_buf[serial->minor], sizeof(g_usbh_serial_vendor_rx_buf[serial->minor]), 0, usbh_pl2303_callback, pl2303_class);
ret = usbh_submit_urb(urb);
if (ret < 0) {
USB_LOG_ERR("usbh_submit_urb failed: %d\n", ret);
}
}
}
void usbh_pl2303_run(struct usbh_pl2303 *pl2303_class)
{
struct usbh_serial *serial;
int ret;
struct usbh_urb *urb = &pl2303_class->bulkin_urb;
serial = usbh_serial_alloc(USBH_SERIAL_TYPE_PL2303);
pl2303_class->user_data = serial;
usbh_serial_register(serial, pl2303_class);
struct cdc_line_coding linecoding;
linecoding.dwDTERate = 115200;
linecoding.bDataBits = 8;
linecoding.bParityType = 0;
linecoding.bCharFormat = 0;
usbh_pl2303_set_line_coding(pl2303_class, &linecoding);
usbh_bulk_urb_fill(urb, pl2303_class->hport, pl2303_class->bulkin, g_usbh_serial_vendor_rx_buf[serial->minor], sizeof(g_usbh_serial_vendor_rx_buf[serial->minor]), 0, usbh_pl2303_callback, pl2303_class);
ret = usbh_submit_urb(urb);
if (ret < 0) {
USB_LOG_ERR("usbh_submit_urb failed: %d\n", ret);
usbh_serial_unregister(serial);
return;
}
}
void usbh_pl2303_stop(struct usbh_pl2303 *pl2303_class)
{
struct usbh_serial *serial;
serial = (struct usbh_serial *)pl2303_class->user_data;
usbh_serial_unregister(serial);
}
#endif

View File

@@ -158,7 +158,7 @@
#define CONFIG_USBHOST_MAX_INTF_ALTSETTINGS 2 #define CONFIG_USBHOST_MAX_INTF_ALTSETTINGS 2
#define CONFIG_USBHOST_MAX_ENDPOINTS 4 #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_HID_CLASS 4
#define CONFIG_USBHOST_MAX_MSC_CLASS 2 #define CONFIG_USBHOST_MAX_MSC_CLASS 2
#define CONFIG_USBHOST_MAX_AUDIO_CLASS 1 #define CONFIG_USBHOST_MAX_AUDIO_CLASS 1
@@ -193,6 +193,10 @@
#define CONFIG_USBHOST_MSC_TIMEOUT 5000 #define CONFIG_USBHOST_MSC_TIMEOUT 5000
#endif #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, /* 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. * you can change to 2K ~ 16K and must be larger than TCP RX windows size in order to avoid being overflow.
*/ */

View File

@@ -47,7 +47,7 @@
#define USBD_MAX_POWER 200 #define USBD_MAX_POWER 200
/* attribute data into no cache ram */ /* 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. /* 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. * 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_INTF_ALTSETTINGS 2
#define CONFIG_USBHOST_MAX_ENDPOINTS 4 #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_HID_CLASS 4
#define CONFIG_USBHOST_MAX_MSC_CLASS 2 #define CONFIG_USBHOST_MAX_MSC_CLASS 2
#define CONFIG_USBHOST_MAX_AUDIO_CLASS 1 #define CONFIG_USBHOST_MAX_AUDIO_CLASS 1
@@ -209,6 +209,10 @@
#define CONFIG_USBHOST_MSC_TIMEOUT 5000 #define CONFIG_USBHOST_MSC_TIMEOUT 5000
#endif #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, /* 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. * you can change to 2K ~ 16K and must be larger than TCP RX windows size in order to avoid being overflow.
*/ */

View File

@@ -17,6 +17,7 @@
#include "hpm_gpio_drv.h" #include "hpm_gpio_drv.h"
#include "shell.h" #include "shell.h"
#include "usbh_core.h" #include "usbh_core.h"
#include "usbh_serial.h"
#include "lwip/tcpip.h" #include "lwip/tcpip.h"
#ifdef CONFIG_USB_EHCI_ISO #ifdef CONFIG_USB_EHCI_ISO
#include "usbh_uvc_stream.h" #include "usbh_uvc_stream.h"
@@ -116,6 +117,7 @@ static void task_start(void *param)
} }
CSH_CMD_EXPORT(lsusb, ); CSH_CMD_EXPORT(lsusb, );
CSH_CMD_EXPORT(usbh_serial, );
#ifdef CONFIG_USB_EHCI_ISO #ifdef CONFIG_USB_EHCI_ISO
// clang-format off // clang-format off