refactor(serial): add host serial framework
Signed-off-by: sakumisu <1203593632@qq.com>
This commit is contained in:
@@ -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
25
Kconfig
@@ -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"
|
||||||
|
|||||||
25
Kconfig.rtt
25
Kconfig.rtt
@@ -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"
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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')
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
|
||||||
};
|
|
||||||
@@ -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
266
class/serial/usbh_cdc_acm.c
Normal 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
|
||||||
|
};
|
||||||
19
class/serial/usbh_cdc_acm.h
Normal file
19
class/serial/usbh_cdc_acm.h
Normal 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
410
class/serial/usbh_ch34x.c
Normal 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
56
class/serial/usbh_ch34x.h
Normal 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
510
class/serial/usbh_cp210x.c
Normal 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
187
class/serial/usbh_cp210x.h
Normal 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
407
class/serial/usbh_ftdi.c
Normal 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
341
class/serial/usbh_ftdi.h
Normal 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
127
class/serial/usbh_gsm.c
Normal 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
726
class/serial/usbh_pl2303.c
Normal 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
|
||||||
|
};
|
||||||
43
class/serial/usbh_pl2303.h
Normal file
43
class/serial/usbh_pl2303.h
Normal 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
719
class/serial/usbh_serial.c
Normal 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
179
class/serial/usbh_serial.h
Normal 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 */
|
||||||
379
class/vendor/serial/usbh_ch34x.c
vendored
379
class/vendor/serial/usbh_ch34x.c
vendored
@@ -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
|
|
||||||
};
|
|
||||||
76
class/vendor/serial/usbh_ch34x.h
vendored
76
class/vendor/serial/usbh_ch34x.h
vendored
@@ -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 */
|
|
||||||
328
class/vendor/serial/usbh_cp210x.c
vendored
328
class/vendor/serial/usbh_cp210x.c
vendored
@@ -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
|
|
||||||
};
|
|
||||||
73
class/vendor/serial/usbh_cp210x.h
vendored
73
class/vendor/serial/usbh_cp210x.h
vendored
@@ -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 */
|
|
||||||
510
class/vendor/serial/usbh_ftdi.c
vendored
510
class/vendor/serial/usbh_ftdi.c
vendored
@@ -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
|
|
||||||
};
|
|
||||||
96
class/vendor/serial/usbh_ftdi.h
vendored
96
class/vendor/serial/usbh_ftdi.h
vendored
@@ -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 */
|
|
||||||
449
class/vendor/serial/usbh_pl2303.c
vendored
449
class/vendor/serial/usbh_pl2303.c
vendored
@@ -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
|
|
||||||
};
|
|
||||||
62
class/vendor/serial/usbh_pl2303.h
vendored
62
class/vendor/serial/usbh_pl2303.h
vendored
@@ -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 */
|
|
||||||
@@ -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))
|
||||||
|
|||||||
155
demo/usb_host.c
155
demo/usb_host.c
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
-----------------
|
-----------------
|
||||||
@@ -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, pl2303,gsm 驱动。当前支持两种使用方式,
|
||||||
|
一种是使用源生 CherryUSB usbhost serial API 进行操作,另一种是基于平台封装的 API 操作,比如 rt-thread device API。,nuttx posix API。
|
||||||
|
|
||||||
|
下面演示的是使用 CherryUSB usbhost serial API 进行串口回环测试,并且使用阻塞发送,异步读取的方式:
|
||||||
|
|
||||||
|
.. code-block:: C
|
||||||
|
|
||||||
|
struct usbh_serial *serial;
|
||||||
|
|
||||||
|
serial = usbh_serial_open("/dev/ttyACM0", USBH_SERIAL_O_RDWR | USBH_SERIAL_O_NONBLOCK);
|
||||||
|
if (serial == NULL) {
|
||||||
|
serial = usbh_serial_open("/dev/ttyUSB0", USBH_SERIAL_O_RDWR | USBH_SERIAL_O_NONBLOCK);
|
||||||
|
if (serial == NULL) {
|
||||||
|
USB_LOG_RAW("no serial device found\r\n");
|
||||||
|
goto delete;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct usbh_serial_termios termios;
|
||||||
|
|
||||||
|
memset(&termios, 0, sizeof(termios));
|
||||||
|
termios.baudrate = 115200;
|
||||||
|
termios.stopbits = 0;
|
||||||
|
termios.parity = 0;
|
||||||
|
termios.databits = 8;
|
||||||
|
termios.rtscts = false;
|
||||||
|
termios.rx_timeout = 0;
|
||||||
|
ret = usbh_serial_control(serial, USBH_SERIAL_CMD_SET_ATTR, &termios);
|
||||||
|
if (ret < 0) {
|
||||||
|
USB_LOG_RAW("set serial attr error, ret:%d\r\n", ret);
|
||||||
|
goto delete_with_close;
|
||||||
|
}
|
||||||
|
|
||||||
|
serial_tx_bytes = 0;
|
||||||
|
while (1) {
|
||||||
|
/* for common, we use timeout with 0xffffffff, this is just a test */
|
||||||
|
ret = usbh_serial_write(serial, serial_tx_buffer, sizeof(serial_tx_buffer));
|
||||||
|
if (ret < 0) {
|
||||||
|
USB_LOG_RAW("serial write error, ret:%d\r\n", ret);
|
||||||
|
goto delete_with_close;
|
||||||
|
} else {
|
||||||
|
serial_tx_bytes += ret;
|
||||||
|
usb_osal_msleep(10); // 11.52 Byte/ms at 115200bps --> 64Byte/5.5ms
|
||||||
|
|
||||||
|
if (serial_tx_bytes == SERIAL_TEST_LEN) {
|
||||||
|
USB_LOG_RAW("send over\r\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
volatile uint32_t wait_timeout = 0;
|
||||||
|
serial_rx_bytes = 0;
|
||||||
|
while (1) {
|
||||||
|
ret = usbh_serial_read(serial, &serial_rx_data[serial_rx_bytes], SERIAL_TEST_LEN);
|
||||||
|
if (ret < 0) {
|
||||||
|
USB_LOG_RAW("serial read error, ret:%d\r\n", ret);
|
||||||
|
goto delete_with_close;
|
||||||
|
} else {
|
||||||
|
serial_rx_bytes += ret;
|
||||||
|
|
||||||
|
if (serial_rx_bytes == SERIAL_TEST_LEN) {
|
||||||
|
USB_LOG_RAW("receive over\r\n");
|
||||||
|
for (uint32_t i = 0; i < SERIAL_TEST_LEN; i++) {
|
||||||
|
if (serial_rx_data[i] != 0xa5) {
|
||||||
|
USB_LOG_RAW("serial loopback data error at index %d, data: 0x%02x\r\n", (unsigned int)i, serial_rx_data[i]);
|
||||||
|
goto delete_with_close;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
USB_LOG_RAW("serial loopback test success\r\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
wait_timeout++;
|
||||||
|
|
||||||
|
if (wait_timeout > 500) { // 5s
|
||||||
|
USB_LOG_RAW("serial read timeout\r\n");
|
||||||
|
goto delete_with_close;
|
||||||
|
}
|
||||||
|
|
||||||
|
usb_osal_msleep(10);
|
||||||
|
}
|
||||||
|
|
||||||
|
usbh_serial_close(serial);
|
||||||
|
|
||||||
|
|
||||||
|
用户需要考虑以下三种场景:
|
||||||
|
|
||||||
|
- USB2TTL 设备 + 启用了波特率,这种情况下需要使用 `usbh_serial_write` 和 `usbh_serial_read` 进行收发数据, **并且需要根据波特率控制发送频率,防止对端丢包**;
|
||||||
|
|
||||||
|
- 纯 USB 设备 + 未启动波特率,这种情况下可以使用 `usbh_serial_cdc_write_async` 和 `usbh_serial_cdc_read_async` 进行异步收发数据,阻塞则用 `usbh_serial_write` 并且不需要控制发送频率。不可以使用 `usbh_serial_read`。
|
||||||
|
|
||||||
|
- 纯 USB 设备 + 启动波特率,同 1,但是速率打折扣。不可以使用 `usbh_serial_cdc_write_async` 和 `usbh_serial_cdc_read_async`。如果是 GSM 设备需要使用第一种。
|
||||||
@@ -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.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
209
platform/rtthread/usbh_rtserial.c
Normal file
209
platform/rtthread/usbh_rtserial.c
Normal 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);
|
||||||
|
}
|
||||||
@@ -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
|
|
||||||
@@ -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.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -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.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user