update(cherryusb): update to v1.6.0
Some checks failed
ToolsCI / Tools (push) Has been cancelled
RT-Thread BSP Static Build Check / 🔍 Summary of Git Diff Changes (push) Has been cancelled
RT-Thread BSP Static Build Check / ${{ matrix.legs.RTT_BSP }} (push) Has been cancelled
RT-Thread BSP Static Build Check / collect-artifacts (push) Has been cancelled
pkgs_test / change (push) Has been cancelled
utest_auto_run / A9 :components/dfs.cfg (push) Has been cancelled
utest_auto_run / A9 :components/lwip.cfg (push) Has been cancelled
utest_auto_run / A9 :components/netdev.cfg (push) Has been cancelled
utest_auto_run / A9 :components/sal.cfg (push) Has been cancelled
utest_auto_run / A9 :cpp11/cpp11.cfg (push) Has been cancelled
utest_auto_run / AARCH64-rtsmart :default.cfg (push) Has been cancelled
utest_auto_run / A9-rtsmart :default.cfg (push) Has been cancelled
utest_auto_run / RISCV-rtsmart :default.cfg (push) Has been cancelled
utest_auto_run / XUANTIE-rtsmart :default.cfg (push) Has been cancelled
utest_auto_run / AARCH64 :default.cfg (push) Has been cancelled
utest_auto_run / AARCH64-smp :default.cfg (push) Has been cancelled
utest_auto_run / A9 :default.cfg (push) Has been cancelled
utest_auto_run / A9-smp :default.cfg (push) Has been cancelled
utest_auto_run / RISCV :default.cfg (push) Has been cancelled
utest_auto_run / RISCV-smp :default.cfg (push) Has been cancelled
utest_auto_run / A9 :kernel/atomic_c11.cfg (push) Has been cancelled
utest_auto_run / RISCV :kernel/atomic_c11.cfg (push) Has been cancelled
utest_auto_run / A9 :kernel/ipc.cfg (push) Has been cancelled
utest_auto_run / A9 :kernel/kernel_basic.cfg (push) Has been cancelled
utest_auto_run / A9 :kernel/mem.cfg (push) Has been cancelled

Signed-off-by: sakumisu <1203593632@qq.com>
This commit is contained in:
sakumisu
2026-01-26 10:59:39 +08:00
committed by R b b666
parent 97e1f014a1
commit cdc50db570
138 changed files with 9846 additions and 11083 deletions

View File

@@ -1 +1 @@
rsource "cherryusb/Kconfig.rtt"
rsource "cherryusb/Kconfig"

View File

@@ -1,4 +1,5 @@
.vscode
build
**/Drivers/**
**/MDK-ARM/DebugConfig/**
**/MDK-ARM/RTE/**

File diff suppressed because it is too large Load Diff

View File

@@ -1,462 +0,0 @@
# Kconfig file for package CherryUSB
menuconfig RT_USING_CHERRYUSB
bool "Using USB with CherryUSB"
default n
if RT_USING_CHERRYUSB
menuconfig RT_CHERRYUSB_DEVICE
bool "Enable usb device mode"
default n
if RT_CHERRYUSB_DEVICE
choice
prompt "Select usb device speed"
default RT_CHERRYUSB_DEVICE_SPEED_FS
config RT_CHERRYUSB_DEVICE_SPEED_FS
bool "FS"
config RT_CHERRYUSB_DEVICE_SPEED_HS
bool "HS"
config RT_CHERRYUSB_DEVICE_SPEED_AUTO
bool "AUTO"
endchoice
choice
prompt "Select usb device ip, and some ip need config in usb_config.h, please check"
default RT_CHERRYUSB_DEVICE_CUSTOM
config RT_CHERRYUSB_DEVICE_CUSTOM
bool "CUSTOM (Implement it yourself)"
config RT_CHERRYUSB_DEVICE_FSDEV_ST
bool "fsdev_st"
config RT_CHERRYUSB_DEVICE_FSDEV_CUSTOM
bool "fsdev_custom"
config RT_CHERRYUSB_DEVICE_DWC2_ST
bool "dwc2_st"
config RT_CHERRYUSB_DEVICE_DWC2_ESP
bool "dwc2_esp"
config RT_CHERRYUSB_DEVICE_DWC2_KENDRYTE
bool "dwc2_kendryte"
config RT_CHERRYUSB_DEVICE_DWC2_AT
bool "dwc2_at"
config RT_CHERRYUSB_DEVICE_DWC2_HC
bool "dwc2_hc"
config RT_CHERRYUSB_DEVICE_DWC2_NATION
bool "dwc2_nation"
config RT_CHERRYUSB_DEVICE_DWC2_GD
bool "dwc2_gd"
config RT_CHERRYUSB_DEVICE_DWC2_CUSTOM
bool "dwc2_custom"
config RT_CHERRYUSB_DEVICE_MUSB_ES
bool "musb_es"
config RT_CHERRYUSB_DEVICE_MUSB_SUNXI
bool "musb_sunxi"
config RT_CHERRYUSB_DEVICE_MUSB_BK
bool "musb_bk"
config RT_CHERRYUSB_DEVICE_MUSB_SIFLI
bool "musb_sifli"
config RT_CHERRYUSB_DEVICE_MUSB_CUSTOM
bool "musb_custom"
config RT_CHERRYUSB_DEVICE_CHIPIDEA_MCX
bool "chipidea_mcx"
config RT_CHERRYUSB_DEVICE_CHIPIDEA_CUSTOM
bool "chipidea_custom"
config RT_CHERRYUSB_DEVICE_KINETIS_MCX
bool "kinetis_mcx"
config RT_CHERRYUSB_DEVICE_KINETIS_MM32
bool "kinetis_mm32"
config RT_CHERRYUSB_DEVICE_KINETIS_CUSTOM
bool "kinetis_custom"
config RT_CHERRYUSB_DEVICE_BL
bool "bouffalo"
config RT_CHERRYUSB_DEVICE_HPM
bool "hpm"
config RT_CHERRYUSB_DEVICE_AIC
bool "aic"
config RT_CHERRYUSB_DEVICE_RP2040
bool "rp2040"
config RT_CHERRYUSB_DEVICE_CH32
bool "ch32"
config RT_CHERRYUSB_DEVICE_PUSB2
bool "pusb2"
config RT_CHERRYUSB_DEVICE_NRF5X
bool "nrf5x"
endchoice
config RT_CHERRYUSB_DEVICE_CDC_ACM
bool
prompt "Enable usb cdc acm device"
default n
config RT_CHERRYUSB_DEVICE_HID
bool
prompt "Enable usb hid device"
default n
config RT_CHERRYUSB_DEVICE_MSC
bool
prompt "Enable usb msc device"
default n
config RT_CHERRYUSB_DEVICE_AUDIO
bool
prompt "Enable usb audio device"
default n
config RT_CHERRYUSB_DEVICE_VIDEO
bool
prompt "Enable usb video device"
default n
config RT_CHERRYUSB_DEVICE_CDC_RNDIS
bool
prompt "Enable usb cdc rndis device"
default n
config RT_CHERRYUSB_DEVICE_CDC_ECM
bool
prompt "Enable usb cdc ecm device"
default n
config RT_CHERRYUSB_DEVICE_CDC_NCM
bool
prompt "Enable usb cdc ncm device"
default n
config RT_CHERRYUSB_DEVICE_MTP
bool
prompt "Enable usb mtp device, it is commercial charge"
default n
config RT_CHERRYUSB_DEVICE_ADB
bool
prompt "Enable usb adb device"
default n
config RT_CHERRYUSB_DEVICE_DFU
bool
prompt "Enable usb dfu device"
default n
config RT_CHERRYUSB_DEVICE_CDC_ACM_CHARDEV
bool
prompt "Enable chardev for cdc acm device"
default n
config CONFIG_USBDEV_REQUEST_BUFFER_LEN
int
prompt "Set device control transfer max buffer size"
default 512
config CONFIG_USBDEV_MSC_MAX_BUFSIZE
int
prompt "Set usb msc device max buffer size"
default 512
help
Set the maximum buffer size for usb msc device, it is used to transfer data.
you can change it to a larger value if you need larger speed but must be a power of blocksize.
config CONFIG_USBDEV_RNDIS_USING_LWIP
bool
prompt "Enable usb rndis device with lwip for lan"
default n
config CONFIG_USBDEV_CDC_ECM_USING_LWIP
bool
prompt "Enable usb cdc ecm device with lwip for lan"
default n
choice
prompt "Select usb device template, please select class driver first"
default RT_CHERRYUSB_DEVICE_TEMPLATE_NONE
config RT_CHERRYUSB_DEVICE_TEMPLATE_NONE
bool
prompt "none (Implement it yourself)"
config RT_CHERRYUSB_DEVICE_TEMPLATE_CDC_ACM
bool
prompt "cdc_acm"
depends on RT_CHERRYUSB_DEVICE_CDC_ACM
config RT_CHERRYUSB_DEVICE_TEMPLATE_MSC
bool
prompt "msc_ram"
depends on RT_CHERRYUSB_DEVICE_MSC
config RT_CHERRYUSB_DEVICE_TEMPLATE_MSC_BLKDEV
bool
prompt "msc_blkdev"
depends on RT_CHERRYUSB_DEVICE_MSC
config RT_CHERRYUSB_DEVICE_TEMPLATE_HID_KEYBOARD
bool
prompt "hid_keyboard"
depends on RT_CHERRYUSB_DEVICE_HID
config RT_CHERRYUSB_DEVICE_TEMPLATE_HID_MOUSE
bool
prompt "hid_mouse"
depends on RT_CHERRYUSB_DEVICE_HID
config RT_CHERRYUSB_DEVICE_TEMPLATE_HID_CUSTOM
bool
prompt "hid_custom"
depends on RT_CHERRYUSB_DEVICE_HID
config RT_CHERRYUSB_DEVICE_TEMPLATE_VIDEO
bool
prompt "video"
depends on RT_CHERRYUSB_DEVICE_VIDEO
config RT_CHERRYUSB_DEVICE_TEMPLATE_AUDIO_V1_MIC_SPEAKER
bool
prompt "audio_v1_mic_speaker_multichan"
depends on RT_CHERRYUSB_DEVICE_AUDIO
config RT_CHERRYUSB_DEVICE_TEMPLATE_AUDIO_V2_MIC_SPEAKER
bool
prompt "audio_v2_mic_speaker_multichan"
depends on RT_CHERRYUSB_DEVICE_AUDIO
config RT_CHERRYUSB_DEVICE_TEMPLATE_CDC_RNDIS
bool
prompt "cdc_rndis"
depends on RT_CHERRYUSB_DEVICE_CDC_RNDIS
config RT_CHERRYUSB_DEVICE_TEMPLATE_CDC_ECM
bool
prompt "cdc_ecm"
depends on RT_CHERRYUSB_DEVICE_CDC_ECM
config RT_CHERRYUSB_DEVICE_TEMPLATE_CDC_NCM
bool
prompt "cdc_ncm"
depends on RT_CHERRYUSB_DEVICE_CDC_NCM
config RT_CHERRYUSB_DEVICE_TEMPLATE_CDC_ACM_MSC
bool
prompt "cdc_acm_msc"
depends on RT_CHERRYUSB_DEVICE_CDC_ACM && RT_CHERRYUSB_DEVICE_MSC
config RT_CHERRYUSB_DEVICE_TEMPLATE_CDC_ACM_MSC_HID
bool
prompt "cdc_acm_msc_hid"
depends on RT_CHERRYUSB_DEVICE_CDC_ACM && RT_CHERRYUSB_DEVICE_MSC && RT_CHERRYUSB_DEVICE_HID
config RT_CHERRYUSB_DEVICE_TEMPLATE_WINUSBV1
bool
prompt "winusbv1"
config RT_CHERRYUSB_DEVICE_TEMPLATE_WINUSBV2_CDC
bool
prompt "winusbv2_cdc"
depends on RT_CHERRYUSB_DEVICE_CDC_ACM
config RT_CHERRYUSB_DEVICE_TEMPLATE_WINUSBV2_HID
bool
prompt "winusbv2_hid"
depends on RT_CHERRYUSB_DEVICE_HID
config RT_CHERRYUSB_DEVICE_TEMPLATE_ADB
bool
prompt "adb"
depends on RT_CHERRYUSB_DEVICE_ADB
config RT_CHERRYUSB_DEVICE_TEMPLATE_CDC_ACM_CHARDEV
bool
prompt "cdc_acm_chardev"
depends on RT_CHERRYUSB_DEVICE_CDC_ACM_CHARDEV
endchoice
config CONFIG_USBDEV_MSC_BLOCK_DEV_NAME
string "usb device msc block device name"
depends on RT_CHERRYUSB_DEVICE_TEMPLATE_MSC_BLKDEV
default "sd0"
endif
menuconfig RT_CHERRYUSB_HOST
bool "Enable usb host mode"
default n
if RT_CHERRYUSB_HOST
choice
prompt "Select usb host ip, and some ip need config in usb_config.h, please check"
default RT_CHERRYUSB_HOST_CUSTOM
config RT_CHERRYUSB_HOST_CUSTOM
bool "CUSTOM (Implement it yourself)"
config RT_CHERRYUSB_HOST_EHCI_BL
bool "ehci_bouffalo"
config RT_CHERRYUSB_HOST_EHCI_HPM
bool "ehci_hpm"
config RT_CHERRYUSB_HOST_EHCI_AIC
bool "ehci_aic"
config RT_CHERRYUSB_HOST_EHCI_MCX
bool "ehci_mcx"
config RT_CHERRYUSB_HOST_EHCI_NUC980
bool "ehci_nuc980"
config RT_CHERRYUSB_HOST_EHCI_MA35D0
bool "ehci_ma35d0"
config RT_CHERRYUSB_HOST_EHCI_CUSTOM
bool "ehci_custom"
config RT_CHERRYUSB_HOST_DWC2_ST
bool "dwc2_st"
config RT_CHERRYUSB_HOST_DWC2_ESP
bool "dwc2_esp"
config RT_CHERRYUSB_HOST_DWC2_KENDRYTE
bool "dwc2_kendryte"
config RT_CHERRYUSB_HOST_DWC2_HC
bool "dwc2_hc"
config RT_CHERRYUSB_HOST_DWC2_NATION
bool "dwc2_nation"
config RT_CHERRYUSB_HOST_DWC2_CUSTOM
bool "dwc2_custom"
config RT_CHERRYUSB_HOST_MUSB_ES
bool "musb_es"
config RT_CHERRYUSB_HOST_MUSB_SUNXI
bool "musb_sunxi"
config RT_CHERRYUSB_HOST_MUSB_BK
bool "musb_bk"
config RT_CHERRYUSB_HOST_MUSB_SIFLI
bool "musb_sifli"
config RT_CHERRYUSB_HOST_MUSB_CUSTOM
bool "musb_custom"
config RT_CHERRYUSB_HOST_PUSB2
bool "pusb2"
config RT_CHERRYUSB_HOST_XHCI
bool "xhci"
config RT_CHERRYUSB_HOST_RP2040
bool "rp2040"
endchoice
config RT_CHERRYUSB_HOST_CDC_ACM
bool
prompt "Enable usb cdc acm driver"
default n
config RT_CHERRYUSB_HOST_HID
bool
prompt "Enable usb hid driver"
default n
config RT_CHERRYUSB_HOST_MSC
bool
prompt "Enable usb msc driver"
default n
select RT_USING_DFS
select RT_USING_DFS_ELMFAT
config RT_CHERRYUSB_HOST_CDC_ECM
bool
prompt "Enable usb cdc ecm driver"
select RT_USING_LWIP
select CONFIG_USBHOST_PLATFORM_CDC_ECM
default n
config RT_CHERRYUSB_HOST_CDC_RNDIS
bool
prompt "Enable usb rndis driver"
select RT_USING_LWIP
select CONFIG_USBHOST_PLATFORM_CDC_RNDIS
default n
config RT_CHERRYUSB_HOST_CDC_NCM
bool
prompt "Enable usb cdc ncm driver"
select RT_USING_LWIP
select CONFIG_USBHOST_PLATFORM_CDC_NCM
default n
config RT_CHERRYUSB_HOST_VIDEO
bool
prompt "Enable usb video driver, it is commercial charge"
default n
config RT_CHERRYUSB_HOST_AUDIO
bool
prompt "Enable usb audio driver, it is commercial charge"
default n
config RT_CHERRYUSB_HOST_BLUETOOTH
bool
prompt "Enable usb bluetooth driver"
default n
config RT_CHERRYUSB_HOST_ASIX
bool
prompt "Enable usb asix driver"
select RT_USING_LWIP
select CONFIG_USBHOST_PLATFORM_ASIX
default n
config RT_CHERRYUSB_HOST_RTL8152
bool
prompt "Enable usb rtl8152 driver"
select RT_USING_LWIP
select CONFIG_USBHOST_PLATFORM_RTL8152
default n
config RT_CHERRYUSB_HOST_FTDI
bool
prompt "Enable usb ftdi driver"
default n
config RT_CHERRYUSB_HOST_CH34X
bool
prompt "Enable usb ch34x driver"
default n
config RT_CHERRYUSB_HOST_CP210X
bool
prompt "Enable usb cp210x driver"
default n
config RT_CHERRYUSB_HOST_PL2303
bool
prompt "Enable usb pl2303 driver"
default n
config CONFIG_USBHOST_PLATFORM_CDC_ECM
bool
config CONFIG_USBHOST_PLATFORM_CDC_RNDIS
bool
config CONFIG_USBHOST_PLATFORM_CDC_NCM
bool
config CONFIG_USBHOST_PLATFORM_ASIX
bool
config CONFIG_USBHOST_PLATFORM_RTL8152
bool
config CONFIG_USBHOST_PSC_PRIO
int
prompt "Set hubport change thread priority, 0 is the max priority"
default 0
config CONFIG_USBHOST_PSC_STACKSIZE
int
prompt "Set hubport change thread stacksize"
default 4096
config CONFIG_USBHOST_REQUEST_BUFFER_LEN
int
prompt "Set host control transfer max buffer size"
default 512
config CONFIG_USBHOST_CONTROL_TRANSFER_TIMEOUT
int
prompt "Set host control transfer timeout, unit is ms"
default 500
config RT_LWIP_PBUF_POOL_BUFSIZE
int "The size of each pbuf in the pbuf pool"
range 1500 2000
default 1600
config CONFIG_USB_DFS_MOUNT_POINT
string "usb host dfs mount point"
depends on RT_CHERRYUSB_HOST_MSC
default "/"
menu "Select USB host template, please select class driver first"
config CONFIG_TEST_USBH_CDC_ACM
int
prompt "demo for test cdc acm, cannot enable this demo, we have used serial framework instead"
default 0
depends on RT_CHERRYUSB_HOST_CDC_ACM
config CONFIG_TEST_USBH_HID
int
prompt "demo for test hid"
default 0
depends on RT_CHERRYUSB_HOST_HID
config CONFIG_TEST_USBH_MSC
int
prompt "demo for test msc, cannot enable this demo, we have used dfs instead"
default 0
depends on RT_CHERRYUSB_HOST_MSC
endmenu
endif
endif

File diff suppressed because it is too large Load Diff

View File

@@ -40,6 +40,8 @@ Taking into account USB performance issues and trying to achieve the theoretical
- Unlimited length make it easier to interface with hardware DMA and take advantage of DMA
- Packetization is handled in interrupt
Performance showhttps://cherryusb.cherry-embedded.org/show/
## Directory Structure
| Directory | Description |
@@ -103,14 +105,15 @@ CherryUSB Host Stack has the following functions:
- Support blocking transfers and asynchronous transfers
- Support Composite Device
- Multi-level HUB support, expandable up to 7 levels(Testing hub with 10 ports works well,only support dwc2/ehci/xhci/rp2040)
- Support Communication Device Class (CDC_ACM, CDC_ECM)
- Support Communication Device Class (CDC_ACM, CDC_ECM, CDC_NCM)
- Support Human Interface Device (HID)
- Support Mass Storage Class (MSC)
- Support USB Video CLASS (UVC1.0, UVC1.5)
- Support USB Audio CLASS (UAC1.0)
- Support Remote NDIS (RNDIS)
- Support USB Bluetooth class (support nimble and zephyr bluetooth stack, support **CLASS:0xE0** or vendor class like cdc acm)
- Support Vendor class (serial, net, wifi)
- Support Vendor Serial Class(CH34X、CP210X、PL2303、FTDI、GSM)
- Support Vendor network Class(RTL8152、AX88772)
- Support USB modeswitch
- Support Android Open Accessory
- Support multi host with the same USB IP
@@ -141,14 +144,14 @@ Among them, `sizeof(struct usbh_hub)` and `sizeof(struct usbh_hubport)` are affe
#define CONFIG_USBHOST_MAX_EXTHUBS 1
#define CONFIG_USBHOST_MAX_EHPORTS 4
#define CONFIG_USBHOST_MAX_INTERFACES 8
#define CONFIG_USBHOST_MAX_INTF_ALTSETTINGS 8
#define CONFIG_USBHOST_MAX_INTF_ALTSETTINGS 2
#define CONFIG_USBHOST_MAX_ENDPOINTS 4
```
x is affected by the following macros:
```
#define CONFIG_USBHOST_MAX_CDC_ACM_CLASS 4
#define CONFIG_USBHOST_MAX_SERIAL_CLASS 4
#define CONFIG_USBHOST_MAX_HID_CLASS 4
#define CONFIG_USBHOST_MAX_MSC_CLASS 2
#define CONFIG_USBHOST_MAX_AUDIO_CLASS 1
@@ -179,32 +182,33 @@ Quickly start, USB basic concepts, API manual, Class basic concepts and examples
## Video Tutorial
CherryUSB Cheese (based V1.4.3): https://www.bilibili.com/cheese/play/ss707687201 .
CherryUSB Cheese (>= V1.4.3): https://www.bilibili.com/cheese/play/ss707687201 .
## Descriptor Generator Tool
TODO
Cherry Descriptor: https://desc.cherry-embedded.org/en
## Demo Repo
| Manufacturer | CHIP or Series | USB IP| Repo Url | Support version | Support status |
| Manufacturer | CHIP or Series | USB IP| Repo Url | Support version | Note |
|:--------------------:|:------------------:|:-----:|:--------:|:------------------:|:-------------:|
|Bouffalolab | BL702/BL616/BL808 | bouffalolab/ehci|[bouffalo_sdk](https://github.com/CherryUSB/bouffalo_sdk)|<= latest | Long-term |
|ST | STM32F1x/STM32F4/STM32H7 | fsdev/dwc2 |[stm32_repo](https://github.com/CherryUSB/cherryusb_stm32)|<= latest | Long-term |
|HPMicro | HPM6000/HPM5000 | hpm/ehci |[hpm_sdk](https://github.com/CherryUSB/hpm_sdk)|<= latest | Long-term |
|Essemi | ES32F36xx | musb |[es32f369_repo](https://github.com/CherryUSB/cherryusb_es32)|<= latest | Long-term |
|Phytium | e2000 | pusb2/xhci |[phytium_repo](https://gitee.com/phytium_embedded/phytium-free-rtos-sdk)|>=1.4.0 | Long-term |
|Artinchip | d12x/d13x/d21x | aic/ehci/ohci |[luban-lite](https://gitee.com/artinchip/luban-lite)|<= latest | Long-term |
|Espressif | esp32s2/esp32s3/esp32p4 | dwc2 |[esp32_repo](https://github.com/CherryUSB/cherryusb_esp32)|<= latest | Long-term |
|NXP | mcx | kinetis/chipidea/ehci |[nxp_mcx_repo](https://github.com/CherryUSB/cherryusb_mcx)|<= latest | Long-term |
|Kendryte | k230 | dwc2 |[k230_repo](https://github.com/CherryUSB/k230_sdk)|v1.2.0 | Long-term |
|Actionstech | ATS30xx | dwc2 |[action_zephyr_repo](https://github.com/CherryUSB/lv_port_actions_technology/tree/master/action_technology_sdk)|>=1.4.0 | Long-term |
|Nationstech | n32h4x | dwc2 |[nation_repo](https://github.com/CherryUSB/cherryusb_nation)|>=1.5.0 | Long-term |
|Raspberry pi | rp2040/rp2350 | rp2040 |[pico-examples](https://github.com/CherryUSB/pico-examples)|<= latest | Long-term |
|AllwinnerTech | F1C100S/F1C200S | musb |[cherryusb_rtt_f1c100s](https://github.com/CherryUSB/cherryusb_rtt_f1c100s)|<= latest | the same with musb |
|Bekencorp | bk7256/bk7258 | musb |[bk_idk](https://github.com/CherryUSB/bk_idk)| v0.7.0 | the same with musb |
|Sophgo | cv18xx | dwc2 |[cvi_alios_open](https://github.com/CherryUSB/cvi_alios_open)| v0.7.0 | TBD |
|WCH | CH32V307/ch58x | ch32_usbfs/ch32_usbhs/ch58x |[wch_repo](https://github.com/CherryUSB/cherryusb_wch)|<= v0.10.2/>=v1.5.0 | TBD |
|Bouffalolab | BL702/BL616/BL808 | bouffalolab/ehci|[bouffalo_sdk](https://github.com/CherryUSB/bouffalo_sdk)|<= latest | Official |
|ST | STM32F1x/STM32F4/STM32H7 | fsdev/dwc2 |[stm32_repo](https://github.com/CherryUSB/cherryusb_stm32)|<= latest | Community |
|HPMicro | HPM6000/HPM5000 | hpm/ehci |[hpm_sdk](https://github.com/CherryUSB/hpm_sdk)|<= latest | Official |
|Essemi | ES32F36xx | musb |[es32f369_repo](https://github.com/CherryUSB/cherryusb_es32)|<= latest | Official |
|Phytium | e2000 | pusb2/xhci |[phytium_repo](https://gitee.com/phytium_embedded/phytium-free-rtos-sdk)|>=1.4.0 | Official |
|Artinchip | d12x/d13x/d21x | aic/ehci/ohci |[luban-lite](https://gitee.com/artinchip/luban-lite)|<= latest | Official |
|Espressif | esp32s2/esp32s3/esp32p4 | dwc2 |[esp32_repo](https://github.com/CherryUSB/cherryusb_esp32)/[espressif](https://github.com/espressif/esp-idf/tree/master/examples/peripherals/usb)|<= latest | Official |
|Kendryte | k230 | dwc2 |[k230_repo](https://github.com/CherryUSB/k230_sdk)|v1.2.0 | Official |
|Actionstech | ATS30xx | dwc2 |[action_zephyr_repo](https://github.com/CherryUSB/lv_port_actions_technology/tree/master/action_technology_sdk)|>=1.4.0 | Official |
|SiFli | SF32LB5x | musb |[SiFli_sdk](https://github.com/OpenSiFli/SiFli-SDK)|>=1.5.0 | Official |
|NXP | mcx | kinetis/chipidea/ehci |[nxp_mcx_repo](https://github.com/CherryUSB/cherryusb_mcx)|<= latest | Community |
|Nationstech | n32h4x | dwc2 |[nation_repo](https://github.com/CherryUSB/cherryusb_nation)|>=1.5.0 | Official ongoing |
|Raspberry pi | rp2040/rp2350 | rp2040 |[pico-sdk](https://github.com/CherryUSB/pico-sdk)|<= latest | Official ongoing |
|AllwinnerTech | F1C100S/F1C200S | musb |[cherryusb_rtt_f1c100s](https://github.com/CherryUSB/cherryusb_rtt_f1c100s)|<= latest | no more update |
|Bekencorp | bk7256/bk7258 | musb |[bk_idk](https://github.com/CherryUSB/bk_idk)| v0.7.0 | Official |
|Sophgo | cv18xx | dwc2 |[cvi_alios_open](https://github.com/CherryUSB/cvi_alios_open)| v0.7.0 | Official |
|WCH | CH32V307/ch58x | ch32_usbfs/ch32_usbhs/ch58x |[wch_repo](https://github.com/CherryUSB/cherryusb_wch)|<= v0.10.2/>=v1.5.0 | no more update |
## Package Support
@@ -226,5 +230,4 @@ CherryUSB discord: https://discord.com/invite/wFfvrSAey8.
Thanks to the following companies for their support (in no particular order):
<img src="docs/assets/bouffalolab.jpg" width="100" height="80"/> <img src="docs/assets/hpmicro.jpg" width="100" height="80" /> <img src="docs/assets/eastsoft.jpg" width="100" height="80" /> <img src="docs/assets/rtthread.jpg" width="100" height="80" /> <img src="docs/assets/sophgo.jpg" width="100" height="80" /> <img src="docs/assets/phytium.jpg" width="100" height="80" /> <img src="docs/assets/thead.jpg" width="100" height="80" /> <img src="docs/assets/nuvoton.jpg" width="100" height="80" /> <img src="docs/assets/artinchip.jpg" width="100" height="80" /> <img src="docs/assets/bekencorp.jpg" width="100" height="80" /> <img src="docs/assets/nxp.png" width="100" height="80" /> <img src="docs/assets/espressif.png" width="100" height="80" /> <img src="docs/assets/canaan.jpg" width="100" height="80" />
<img src="docs/assets/actions.jpg" width="100" height="80" /> <img src="docs/assets/nationstech.jpg" width="100" height="80" />
<img src="docs/assets/bouffalolab.jpg" width="100" height="80"/> <img src="docs/assets/hpmicro.jpg" width="100" height="80" /> <img src="docs/assets/eastsoft.jpg" width="100" height="80" /> <img src="docs/assets/rtthread.jpg" width="100" height="80" /> <img src="docs/assets/sophgo.jpg" width="100" height="80" /> <img src="docs/assets/phytium.jpg" width="100" height="80" /> <img src="docs/assets/thead.jpg" width="100" height="80" /> <img src="docs/assets/nuvoton.jpg" width="100" height="80" /> <img src="docs/assets/artinchip.jpg" width="100" height="80" /> <img src="docs/assets/bekencorp.jpg" width="100" height="80" /> <img src="docs/assets/nxp.png" width="100" height="80" /> <img src="docs/assets/espressif.png" width="100" height="80" /> <img src="docs/assets/canaan.jpg" width="100" height="80" /> <img src="docs/assets/actions.jpg" width="100" height="80" /> <img src="docs/assets/sifli.jpg" width="100" height="80" /> <img src="docs/assets/nationstech.jpg" width="100" height="80" />

View File

@@ -112,7 +112,8 @@ CherryUSB Host 协议栈当前实现以下功能:
- Support USB Audio CLASS (UAC1.0)
- 支持 Remote NDIS (RNDIS)
- 支持 USB Bluetooth (支持 nimble and zephyr bluetooth 协议栈,支持 **CLASS: 0xE0** 或者厂家自定义类,类似于 cdc acm 功能)
- 支持 Vendor 类 class (serial, net, wifi)
- 支持 Vendor Serial 类(CH34X、CP210X、PL2303、FTDI、GSM)
- 支持 Vendor network 类(RTL8152、AX88772)
- 支持 USB modeswitch
- 支持 Android Open Accessory
- 支持相同 USB IP 的多主机
@@ -150,7 +151,7 @@ CherryUSB Host 协议栈资源占用说明GCC 10.2 with -O2关闭 log
x 受以下宏影响:
```
#define CONFIG_USBHOST_MAX_CDC_ACM_CLASS 4
#define CONFIG_USBHOST_MAX_SERIAL_CLASS 4
#define CONFIG_USBHOST_MAX_HID_CLASS 4
#define CONFIG_USBHOST_MAX_MSC_CLASS 2
#define CONFIG_USBHOST_MAX_AUDIO_CLASS 1
@@ -181,11 +182,11 @@ CherryUSB 快速入门、USB 基本概念、API 手册、Class 基本概念和
## 视频教程
CherryUSB 课程(基于 V1.4.3https://www.bilibili.com/cheese/play/ss707687201 。
CherryUSB 课程(>= V1.4.3https://www.bilibili.com/cheese/play/ss707687201 。
## 描述符生成工具
TODO
Cherry Descriptor: https://desc.cherry-embedded.org/zh
## 示例仓库
@@ -197,7 +198,7 @@ TODO
|Essemi | ES32F36xx | musb |[es32f369_repo](https://github.com/CherryUSB/cherryusb_es32)|<= latest | Official |
|Phytium | e2000 | pusb2/xhci |[phytium_repo](https://gitee.com/phytium_embedded/phytium-free-rtos-sdk)|>=1.4.0 | Official |
|Artinchip | d12x/d13x/d21x | aic/ehci/ohci |[luban-lite](https://gitee.com/artinchip/luban-lite)|<= latest | Official |
|Espressif | esp32s2/esp32s3/esp32p4 | dwc2 |[esp32_repo](https://github.com/CherryUSB/cherryusb_esp32)|<= latest | Official ongoing |
|Espressif | esp32s2/esp32s3/esp32p4 | dwc2 |[esp32_repo](https://github.com/CherryUSB/cherryusb_esp32)/[espressif](https://github.com/espressif/esp-idf/tree/master/examples/peripherals/usb)|<= latest | Official |
|Kendryte | k230 | dwc2 |[k230_repo](https://github.com/CherryUSB/k230_sdk)|v1.2.0 | Official |
|Actionstech | ATS30xx | dwc2 |[action_zephyr_repo](https://github.com/CherryUSB/lv_port_actions_technology/tree/master/action_technology_sdk)|>=1.4.0 | Official |
|SiFli | SF32LB5x | musb |[SiFli_sdk](https://github.com/OpenSiFli/SiFli-SDK)|>=1.5.0 | Official |

View File

@@ -14,9 +14,8 @@ path += [cwd + '/class/wireless']
path += [cwd + '/class/midi']
path += [cwd + '/class/adb']
path += [cwd + '/class/dfu']
path += [cwd + '/class/midi']
path += [cwd + '/class/serial']
path += [cwd + '/class/vendor/net']
path += [cwd + '/class/vendor/serial']
path += [cwd + '/class/vendor/wifi']
src = []
@@ -47,6 +46,9 @@ if GetDepend(['RT_CHERRYUSB_DEVICE']):
if GetDepend(['RT_CHERRYUSB_DEVICE_DWC2_KENDRYTE']):
src += Glob('port/dwc2/usb_dc_dwc2.c')
src += Glob('port/dwc2/usb_glue_kendryte.c')
if GetDepend(['RT_CHERRYUSB_DEVICE_DWC2_INFINEON']):
src += Glob('port/dwc2/usb_dc_dwc2.c')
src += Glob('port/dwc2/usb_glue_infineon.c')
if GetDepend(['RT_CHERRYUSB_DEVICE_DWC2_AT']):
src += Glob('port/dwc2/usb_dc_dwc2.c')
src += Glob('port/dwc2/usb_glue_at.c')
@@ -112,6 +114,9 @@ if GetDepend(['RT_CHERRYUSB_DEVICE']):
LIBS = ['libpusb2_dc_a32_softfp_neon.a']
if GetDepend(['RT_CHERRYUSB_DEVICE_NRF5X']):
src += Glob('port/nrf5x/usb_dc_nrf5x.c')
if GetDepend(['RT_CHERRYUSB_DEVICE_RP2040']):
path += [cwd + '/port/rp2040']
src += Glob('port/rp2040/usb_dc_rp2040.c')
if GetDepend(['RT_CHERRYUSB_DEVICE_CDC_ACM']):
src += Glob('class/cdc/usbd_cdc_acm.c')
@@ -166,10 +171,12 @@ if GetDepend(['RT_CHERRYUSB_DEVICE']):
src += Glob('demo/cdc_acm_hid_msc_template.c')
if GetDepend(['RT_CHERRYUSB_DEVICE_TEMPLATE_WINUSBV1']):
src += Glob('demo/winusb1.0_template.c')
if GetDepend(['RT_CHERRYUSB_DEVICE_TEMPLATE_WINUSBV2']):
src += Glob('demo/winusb2.0_template.c')
if GetDepend(['RT_CHERRYUSB_DEVICE_TEMPLATE_WINUSBV2_CDC']):
src += Glob('demo/winusb2.0_cdc_template.c')
if GetDepend(['RT_CHERRYUSB_DEVICE_TEMPLATE_WINUSBV2_HID']):
src += Glob('demo/winusb2.0_hid_template.c')
if GetDepend(['RT_CHERRYUSB_DEVICE_TEMPLATE_WEBUSB_HID']):
src += Glob('demo/webusb_hid_template.c')
if GetDepend(['RT_CHERRYUSB_DEVICE_TEMPLATE_ADB']):
src += Glob('demo/adb/usbd_adb_template.c')
if GetDepend(['RT_CHERRYUSB_DEVICE_TEMPLATE_CDC_ACM_CHARDEV']):
@@ -216,6 +223,12 @@ if GetDepend(['RT_CHERRYUSB_HOST']):
if GetDepend(['RT_CHERRYUSB_HOST_DWC2_KENDRYTE']):
src += Glob('port/dwc2/usb_hc_dwc2.c')
src += Glob('port/dwc2/usb_glue_kendryte.c')
if GetDepend(['RT_CHERRYUSB_HOST_DWC2_INFINEON']):
src += Glob('port/dwc2/usb_hc_dwc2.c')
src += Glob('port/dwc2/usb_glue_infineon.c')
if GetDepend(['RT_CHERRYUSB_HOST_DWC2_AT']):
src += Glob('port/dwc2/usb_hc_dwc2.c')
src += Glob('port/dwc2/usb_glue_at.c')
if GetDepend(['RT_CHERRYUSB_HOST_DWC2_HC']):
src += Glob('port/dwc2/usb_hc_dwc2.c')
src += Glob('port/dwc2/usb_glue_hc.c')
@@ -266,8 +279,12 @@ if GetDepend(['RT_CHERRYUSB_HOST']):
LIBPATH = [cwd + '/port/xhci/phytium']
LIBS = ['libxhci_a32_softfp_neon.a']
if GetDepend(['RT_CHERRYUSB_HOST_RP2040']):
path += [cwd + '/port/rp2040']
src += Glob('port/rp2040/usb_hc_rp2040.c')
if GetDepend(['RT_CHERRYUSB_HOST_CDC_ACM']):
src += Glob('class/cdc/usbh_cdc_acm.c')
src += Glob('class/serial/usbh_cdc_acm.c')
if GetDepend(['RT_CHERRYUSB_HOST_HID']):
src += Glob('class/hid/usbh_hid.c')
if GetDepend(['RT_CHERRYUSB_HOST_MSC']):
@@ -289,23 +306,26 @@ if GetDepend(['RT_CHERRYUSB_HOST']):
if GetDepend(['RT_CHERRYUSB_HOST_RTL8152']):
src += Glob('class/vendor/net/usbh_rtl8152.c')
if GetDepend(['RT_CHERRYUSB_HOST_FTDI']):
src += Glob('class/vendor/serial/usbh_ftdi.c')
src += Glob('class/serial/usbh_ftdi.c')
if GetDepend(['RT_CHERRYUSB_HOST_CH34X']):
src += Glob('class/vendor/serial/usbh_ch34x.c')
src += Glob('class/serial/usbh_ch34x.c')
if GetDepend(['RT_CHERRYUSB_HOST_CP210X']):
src += Glob('class/vendor/serial/usbh_cp210x.c')
src += Glob('class/serial/usbh_cp210x.c')
if GetDepend(['RT_CHERRYUSB_HOST_PL2303']):
src += Glob('class/vendor/serial/usbh_pl2303.c')
src += Glob('class/serial/usbh_pl2303.c')
if GetDepend(['CONFIG_TEST_USBH_HID']):
if GetDepend(['RT_TEST_USBH_HID']):
CPPDEFINES+=['CONFIG_TEST_USBH_HID']
src += Glob('demo/usb_host.c')
if GetDepend(['RT_CHERRYUSB_HOST_CDC_ACM']) \
or GetDepend(['RT_CHERRYUSB_HOST_FTDI']) \
or GetDepend(['RT_CHERRYUSB_HOST_CH34X']) \
or GetDepend(['RT_CHERRYUSB_HOST_CP210X']) \
or GetDepend(['RT_CHERRYUSB_HOST_PL2303']):
src += Glob('platform/rtthread/usbh_serial.c')
or GetDepend(['RT_CHERRYUSB_HOST_PL2303']) \
or GetDepend(['RT_CHERRYUSB_HOST_GSM']):
src += Glob('class/serial/usbh_serial.c')
src += Glob('platform/rtthread/usbh_rtserial.c')
if GetDepend('RT_USING_DFS') and GetDepend(['RT_CHERRYUSB_HOST_MSC']):
src += Glob('platform/rtthread/usbh_dfs.c')

View File

@@ -1,5 +1,5 @@
VERSION_MAJOR = 1
VERSION_MINOR = 5
PATCHLEVEL = 2
VERSION_MINOR = 6
PATCHLEVEL = 0
VERSION_TWEAK = 0
EXTRAVERSION = 0

View File

@@ -47,10 +47,11 @@ list(
${CMAKE_CURRENT_LIST_DIR}/class/midi
${CMAKE_CURRENT_LIST_DIR}/class/adb
${CMAKE_CURRENT_LIST_DIR}/class/dfu
${CMAKE_CURRENT_LIST_DIR}/class/serial
${CMAKE_CURRENT_LIST_DIR}/class/vendor/net
${CMAKE_CURRENT_LIST_DIR}/class/vendor/serial
${CMAKE_CURRENT_LIST_DIR}/class/vendor/wifi
${CMAKE_CURRENT_LIST_DIR}/class/aoa
${CMAKE_CURRENT_LIST_DIR}/class/gamepad
)
if(CONFIG_CHERRYUSB_DEVICE)
@@ -85,6 +86,9 @@ if(CONFIG_CHERRYUSB_DEVICE)
if(CONFIG_CHERRYUSB_DEVICE_ADB)
list(APPEND cherryusb_srcs ${CMAKE_CURRENT_LIST_DIR}/class/adb/usbd_adb.c)
endif()
if(CONFIG_CHERRYUSB_DEVICE_GAMEPAD)
list(APPEND cherryusb_srcs ${CMAKE_CURRENT_LIST_DIR}/class/gamepad/usbd_gamepad.c)
endif()
if(CONFIG_CHERRYUSB_DEVICE_FSDEV_ST)
list(APPEND cherryusb_srcs ${CMAKE_CURRENT_LIST_DIR}/port/fsdev/usb_dc_fsdev.c)
@@ -156,7 +160,7 @@ if(CONFIG_CHERRYUSB_HOST)
)
if(CONFIG_CHERRYUSB_HOST_CDC_ACM)
list(APPEND cherryusb_srcs ${CMAKE_CURRENT_LIST_DIR}/class/cdc/usbh_cdc_acm.c)
list(APPEND cherryusb_srcs ${CMAKE_CURRENT_LIST_DIR}/class/serial/usbh_cdc_acm.c)
endif()
if(CONFIG_CHERRYUSB_HOST_CDC_ECM)
list(APPEND cherryusb_srcs ${CMAKE_CURRENT_LIST_DIR}/class/cdc/usbh_cdc_ecm.c)
@@ -235,30 +239,39 @@ if(CONFIG_CHERRYUSB_HOST)
list(APPEND cherryusb_srcs ${CMAKE_CURRENT_LIST_DIR}/class/vendor/net/usbh_rtl8152.c)
endif()
if(CONFIG_CHERRYUSB_HOST_CH34X)
list(APPEND cherryusb_srcs ${CMAKE_CURRENT_LIST_DIR}/class/vendor/serial/usbh_ch34x.c)
list(APPEND cherryusb_srcs ${CMAKE_CURRENT_LIST_DIR}/class/serial/usbh_ch34x.c)
endif()
if(CONFIG_CHERRYUSB_HOST_CP210X)
list(APPEND cherryusb_srcs ${CMAKE_CURRENT_LIST_DIR}/class/vendor/serial/usbh_cp210x.c)
list(APPEND cherryusb_srcs ${CMAKE_CURRENT_LIST_DIR}/class/serial/usbh_cp210x.c)
endif()
if(CONFIG_CHERRYUSB_HOST_FTDI)
list(APPEND cherryusb_srcs ${CMAKE_CURRENT_LIST_DIR}/class/vendor/serial/usbh_ftdi.c)
list(APPEND cherryusb_srcs ${CMAKE_CURRENT_LIST_DIR}/class/serial/usbh_ftdi.c)
endif()
if(CONFIG_CHERRYUSB_HOST_PL2303)
list(APPEND cherryusb_srcs ${CMAKE_CURRENT_LIST_DIR}/class/vendor/serial/usbh_pl2303.c)
list(APPEND cherryusb_srcs ${CMAKE_CURRENT_LIST_DIR}/class/serial/usbh_pl2303.c)
endif()
if(CONFIG_CHERRYUSB_HOST_BL616)
list(APPEND cherryusb_srcs ${CMAKE_CURRENT_LIST_DIR}/class/vendor/wifi/usbh_bl616.c)
if(CONFIG_CHERRYUSB_HOST_GSM)
list(APPEND cherryusb_srcs ${CMAKE_CURRENT_LIST_DIR}/class/serial/usbh_gsm.c)
endif()
if(CONFIG_CHERRYUSB_HOST_AOA)
list(APPEND cherryusb_srcs ${CMAKE_CURRENT_LIST_DIR}/class/aoa/usbh_aoa.c)
endif()
if(CONFIG_CHERRYUSB_HOST_CDC_ACM
OR CONFIG_CHERRYUSB_HOST_CH34X
OR CONFIG_CHERRYUSB_HOST_CP210X
OR CONFIG_CHERRYUSB_HOST_FTDI
OR CONFIG_CHERRYUSB_HOST_PL2303
OR CONFIG_CHERRYUSB_HOST_GSM
)
list(APPEND cherryusb_srcs ${CMAKE_CURRENT_LIST_DIR}/class/serial/usbh_serial.c)
endif()
if(CONFIG_CHERRYUSB_HOST_CDC_ECM
OR CONFIG_CHERRYUSB_HOST_CDC_RNDIS
OR CONFIG_CHERRYUSB_HOST_CDC_NCM
OR CONFIG_CHERRYUSB_HOST_ASIX
OR CONFIG_CHERRYUSB_HOST_RTL8152
OR CONFIG_CHERRYUSB_HOST_BL616
)
if("${CONFIG_CHERRYUSB_OSAL}" STREQUAL "idf")
list(APPEND cherryusb_srcs ${CMAKE_CURRENT_LIST_DIR}/platform/idf/usbh_net.c)
@@ -335,11 +348,15 @@ if(CONFIG_CHERRYUSB_HOST)
list(APPEND cherryusb_srcs ${CMAKE_CURRENT_LIST_DIR}/port/rp2040/usb_hc_rp2040.c)
endif()
if(CONFIG_TEST_USBH_CDC_ACM OR CONFIG_TEST_USBH_HID OR CONFIG_TEST_USBH_MSC)
if(CONFIG_TEST_USBH_SERIAL OR CONFIG_TEST_USBH_HID OR CONFIG_TEST_USBH_MSC)
list(APPEND cherryusb_srcs ${CMAKE_CURRENT_LIST_DIR}/demo/usb_host.c)
endif()
endif()
if(CONFIG_CHERRYUSB_DEVICE AND CONFIG_CHERRYUSB_HOST)
list(APPEND cherryusb_srcs ${CMAKE_CURRENT_LIST_DIR}/core/usbotg_core.c)
endif()
if(DEFINED CONFIG_CHERRYUSB_OSAL)
if("${CONFIG_CHERRYUSB_OSAL}" STREQUAL "freertos")
list(APPEND cherryusb_srcs ${CMAKE_CURRENT_LIST_DIR}/osal/usb_osal_freertos.c)

View File

@@ -157,7 +157,7 @@
#define CONFIG_USBHOST_MAX_INTF_ALTSETTINGS 2
#define CONFIG_USBHOST_MAX_ENDPOINTS 4
#define CONFIG_USBHOST_MAX_CDC_ACM_CLASS 4
#define CONFIG_USBHOST_MAX_SERIAL_CLASS 4
#define CONFIG_USBHOST_MAX_HID_CLASS 4
#define CONFIG_USBHOST_MAX_MSC_CLASS 2
#define CONFIG_USBHOST_MAX_AUDIO_CLASS 1
@@ -188,6 +188,10 @@
#define CONFIG_USBHOST_CONTROL_TRANSFER_TIMEOUT 500
#endif
#ifndef CONFIG_USBHOST_SERIAL_RX_SIZE
#define CONFIG_USBHOST_SERIAL_RX_SIZE 2048
#endif
#ifndef CONFIG_USBHOST_MSC_TIMEOUT
#define CONFIG_USBHOST_MSC_TIMEOUT 5000
#endif
@@ -301,6 +305,7 @@
/* ---------------- MUSB Configuration ---------------- */
#define CONFIG_USB_MUSB_PIPE_NUM 8
// #define CONFIG_USB_MUSB_SUNXI
// #define CONFIG_USB_MUSB_WITHOUT_MULTIPOINT
/* When your chip hardware supports high-speed and wants to initialize it in high-speed mode,
* the relevant IP will configure the internal or external high-speed PHY according to CONFIG_USB_HS.
@@ -317,4 +322,7 @@
#define usb_ramaddr2phyaddr(addr) (addr)
#endif
/* Enable OTG support, only support hpmicro now */
// #define CONFIG_USB_OTG_ENABLE
#endif

View File

@@ -176,9 +176,6 @@ int usbh_aoa_register_hid(struct usbh_aoa *aoa_class, uint16_t id, uint8_t *repo
int usbh_aoa_send_hid_event(struct usbh_aoa *aoa_class, uint16_t id, uint8_t *event, uint32_t event_len)
{
struct usb_setup_packet *setup;
int ret;
uint8_t len;
uint32_t offset;
if (!aoa_class || !aoa_class->hport) {
return -USB_ERR_INVAL;
@@ -198,7 +195,6 @@ int usbh_aoa_send_hid_event(struct usbh_aoa *aoa_class, uint16_t id, uint8_t *ev
static int usbh_aoa_connect(struct usbh_hubport *hport, uint8_t intf)
{
struct usb_endpoint_descriptor *ep_desc;
int ret = 0;
struct usbh_aoa *aoa_class = &g_aoa_class;

View File

@@ -88,18 +88,6 @@
#define AUDIO_ENDPOINT_UNDEFINED 0x00U
#define AUDIO_ENDPOINT_GENERAL 0x01U
/* Feature Unit Control Bits */
#define AUDIO_CONTROL_MUTE 0x0001
#define AUDIO_CONTROL_VOLUME 0x0002
#define AUDIO_CONTROL_BASS 0x0004
#define AUDIO_CONTROL_MID 0x0008
#define AUDIO_CONTROL_TREBLE 0x0010
#define AUDIO_CONTROL_GRAPHIC_EQUALIZER 0x0020
#define AUDIO_CONTROL_AUTOMATIC_GAIN 0x0040
#define AUDIO_CONTROL_DEALY 0x0080
#define AUDIO_CONTROL_BASS_BOOST 0x0100
#define AUDIO_CONTROL_LOUDNESS 0x0200
/* Encoder Type Codes */
#define AUDIO_ENCODER_UNDEF 0x00
#define AUDIO_ENCODER_OTHER 0x01
@@ -245,22 +233,34 @@
#define AUDIO_FU_CONTROL_OVERFLOW 0x0f
#define AUDIO_FU_CONTROL_LATENCY 0x10
#define AUDIO_V2_FU_CONTROL_UNDEF 0x00
#define AUDIO_V2_FU_CONTROL_MUTE (0x03 << 0)
#define AUDIO_V2_FU_CONTROL_VOLUME (0x03 << 2)
#define AUDIO_V2_FU_CONTROL_BASS (0x03 << 4)
#define AUDIO_V2_FU_CONTROL_MID (0x03 << 6)
#define AUDIO_V2_FU_CONTROL_TREBLE (0x03 << 8)
#define AUDIO_V2_FU_CONTROL_EQUALIZER (0x03 << 10)
#define AUDIO_V2_FU_CONTROL_AGC (0x03 << 12)
#define AUDIO_V2_FU_CONTROL_DELAY (0x03 << 14)
#define AUDIO_V2_FU_CONTROL_BASS_BOOST (0x03 << 16)
#define AUDIO_V2_FU_CONTROL_LOUDNESS (0x03 << 18)
#define AUDIO_V2_FU_CONTROL_INP_GAIN (0x03 << 20)
#define AUDIO_V2_FU_CONTROL_INP_GAIN_PAD (0x03 << 22)
#define AUDIO_V2_FU_CONTROL_PHASE_INVERT (0x03 << 24)
#define AUDIO_V2_FU_CONTROL_UNDERFLOW (0x03 << 26)
#define AUDIO_V2_FU_CONTROL_OVERFLOW (0x03 << 28)
/* Feature Unit Control Bits */
#define AUDIO_CONTROL_MUTE 0x0001
#define AUDIO_CONTROL_VOLUME 0x0002
#define AUDIO_CONTROL_BASS 0x0004
#define AUDIO_CONTROL_MID 0x0008
#define AUDIO_CONTROL_TREBLE 0x0010
#define AUDIO_CONTROL_GRAPHIC_EQUALIZER 0x0020
#define AUDIO_CONTROL_AUTOMATIC_GAIN 0x0040
#define AUDIO_CONTROL_DEALY 0x0080
#define AUDIO_CONTROL_BASS_BOOST 0x0100
#define AUDIO_CONTROL_LOUDNESS 0x0200
#define AUDIO_V2_CONTROL_UNDEF 0x00
#define AUDIO_V2_CONTROL_MUTE (0x03 << 0)
#define AUDIO_V2_CONTROL_VOLUME (0x03 << 2)
#define AUDIO_V2_CONTROL_BASS (0x03 << 4)
#define AUDIO_V2_CONTROL_MID (0x03 << 6)
#define AUDIO_V2_CONTROL_TREBLE (0x03 << 8)
#define AUDIO_V2_CONTROL_EQUALIZER (0x03 << 10)
#define AUDIO_V2_CONTROL_AGC (0x03 << 12)
#define AUDIO_V2_CONTROL_DELAY (0x03 << 14)
#define AUDIO_V2_CONTROL_BASS_BOOST (0x03 << 16)
#define AUDIO_V2_CONTROL_LOUDNESS (0x03 << 18)
#define AUDIO_V2_CONTROL_INP_GAIN (0x03 << 20)
#define AUDIO_V2_CONTROL_INP_GAIN_PAD (0x03 << 22)
#define AUDIO_V2_CONTROL_PHASE_INVERT (0x03 << 24)
#define AUDIO_V2_CONTROL_UNDERFLOW (0x03 << 26)
#define AUDIO_V2_CONTROL_OVERFLOW (0x03 << 28)
/* Parametric Equalizer Section Effect Unit Control Selectors */
#define AUDIO_PE_CONTROL_UNDEF 0x00
@@ -605,7 +605,7 @@ struct audio_cs_if_ac_header_descriptor {
uint8_t baInterfaceNr[];
} __PACKED;
#define AUDIO_SIZEOF_AC_HEADER_DESC(n) (8 + n)
#define AUDIO_SIZEOF_AC_HEADER_DESC(bInCollection) (8 + (bInCollection))
struct audio_cs_if_ac_input_terminal_descriptor {
uint8_t bLength;
@@ -646,7 +646,7 @@ struct audio_cs_if_ac_feature_unit_descriptor {
uint8_t iFeature;
} __PACKED;
#define AUDIO_SIZEOF_AC_FEATURE_UNIT_DESC(ch, n) (7 + (ch + 1) * n)
#define AUDIO_SIZEOF_AC_FEATURE_UNIT_DESC(bNrChannels, bControlSize) (7 + ((bNrChannels) + 1) * (bControlSize))
struct audio_cs_if_ac_selector_unit_descriptor {
uint8_t bLength;
@@ -658,7 +658,7 @@ struct audio_cs_if_ac_selector_unit_descriptor {
uint8_t iSelector;
} __PACKED;
#define AUDIO_SIZEOF_AC_SELECTOR_UNIT_DESC(n) (6 + n)
#define AUDIO_SIZEOF_AC_SELECTOR_UNIT_DESC(bNrInPins) (6 + (bNrInPins))
struct audio_cs_if_as_general_descriptor {
uint8_t bLength;
@@ -683,7 +683,7 @@ struct audio_cs_if_as_format_type_descriptor {
uint8_t tSamFreq[3];
} __PACKED;
#define AUDIO_SIZEOF_FORMAT_TYPE_DESC(n) (8 + 3 * n)
#define AUDIO_SIZEOF_FORMAT_TYPE_DESC(bSamFreqType) (8 + 3 * (bSamFreqType))
struct audio_ep_descriptor {
uint8_t bLength;
@@ -738,7 +738,7 @@ struct audio_cs_ep_ep_general_descriptor {
PP_NARG(__VA_ARGS__), /* bInCollection */ \
__VA_ARGS__ /* baInterfaceNr */
#define AUDIO_AC_DESCRIPTOR_INIT_LEN(n) (0x08 + 0x09 + 0x08 + n)
#define AUDIO_AC_DESCRIPTOR_LEN(bInCollection) (0x08 + 0x09 + 0x08 + bInCollection)
#define AUDIO_AC_INPUT_TERMINAL_DESCRIPTOR_INIT(bTerminalID, wTerminalType, bNrChannels, wChannelConfig) \
0x0C, /* bLength */ \
@@ -880,8 +880,8 @@ struct audio_cs_ep_ep_general_descriptor {
0x03, /* bRefresh, 8ms */ \
0x00 /* bSynchAddress */
#define AUDIO_AS_DESCRIPTOR_INIT_LEN(n) (0x09 + 0x09 + 0x07 + 0x08 + 3 * n + 0x09 + 0x07)
#define AUDIO_AS_FEEDBACK_DESCRIPTOR_INIT_LEN(n) (0x09 + 0x09 + 0x07 + 0x08 + 3 * n + 0x09 + 0x07 + 0x09)
#define AUDIO_AS_DESCRIPTOR_LEN(bSamFreqType) (0x09 + 0x09 + 0x07 + 0x08 + 3 * (bSamFreqType) + 0x09 + 0x07)
#define AUDIO_AS_FEEDBACK_DESCRIPTOR_LEN(bSamFreqType) (0x09 + 0x09 + 0x07 + 0x08 + 3 * (bSamFreqType) + 0x09 + 0x07 + 0x09)
#define AUDIO_AS_ALTSETTING_DESCRIPTOR_INIT(bInterfaceNumber, bAlternateSetting, bTerminalLink, bNrChannels, bSubFrameSize, bBitResolution, bEndpointAddress, bmAttributes, wMaxPacketSize, bInterval, ...) \
0x09, /* bLength */ \
@@ -924,9 +924,9 @@ struct audio_cs_ep_ep_general_descriptor {
0x00, /* wLockDelay */ \
0x00
#define AUDIO_AS_ALTSETTING_DESCRIPTOR_INIT_LEN(n) (0x09 + 0x07 + 0x08 + 3 * n + 0x09 + 0x07)
#define AUDIO_AS_ALTSETTING_DESCRIPTOR_LEN(bSamFreqType) (0x09 + 0x07 + 0x08 + 3 * (bSamFreqType) + 0x09 + 0x07)
#define AUDIO_AS_ALTSETTING0_DESCRIPTOR_INIT(bInterfaceNumber) \
#define AUDIO_AS_ALTSETTING0_DESCRIPTOR_INIT(bInterfaceNumber) \
0x09, /* bLength */ \
USB_DESCRIPTOR_TYPE_INTERFACE, /* bDescriptorType */ \
bInterfaceNumber, /* bInterfaceNumber */ \
@@ -937,19 +937,6 @@ struct audio_cs_ep_ep_general_descriptor {
AUDIO_PROTOCOL_UNDEFINED, /* bInterfaceProtocol */ \
0x00 /* iInterface */
#define AUDIO_MS_STANDARD_DESCRIPTOR_INIT(bInterfaceNumber, bNumEndpoints) \
0x09, /* bLength */ \
USB_DESCRIPTOR_TYPE_INTERFACE, /* bDescriptorType */ \
bInterfaceNumber, /* bInterfaceNumber */ \
0x00, /* bAlternateSetting */ \
bNumEndpoints, /* bNumEndpoints */ \
USB_DEVICE_CLASS_AUDIO, /* bInterfaceClass */ \
AUDIO_SUBCLASS_MIDISTREAMING, /* bInterfaceSubClass */ \
AUDIO_PROTOCOL_UNDEFINED, /* bInterfaceProtocol */ \
0x00 /* iInterface */
#define AUDIO_MS_STANDARD_DESCRIPTOR_INIT_LEN 0x09
struct audio_v2_channel_cluster_descriptor {
uint8_t bNrChannels;
uint32_t bmChannelConfig;
@@ -993,7 +980,7 @@ struct audio_v2_cs_if_ac_clock_selector_descriptor {
uint8_t iClockSelector;
} __PACKED;
#define AUDIO_SIZEOF_AC_CLOCK_SELECTOR_DESC(n) (7 + n)
#define AUDIO_SIZEOF_AC_CLOCK_SELECTOR_DESC(bNrInPins) (7 + (bNrInPins))
struct audio_v2_cs_if_ac_clock_multiplier_descriptor {
uint8_t bLength;
@@ -1005,7 +992,7 @@ struct audio_v2_cs_if_ac_clock_multiplier_descriptor {
uint8_t iClockMultiplier;
} __PACKED;
#define AUDIO_SIZEOF_AC_CLOCK_MULTIPLIER_DESC() (7)
#define AUDIO_SIZEOF_AC_CLOCK_MULTIPLIER_DESC (7)
struct audio_v2_cs_if_ac_input_terminal_descriptor {
uint8_t bLength;
@@ -1049,7 +1036,7 @@ struct audio_v2_cs_if_ac_feature_unit_descriptor {
uint8_t iFeature;
} __PACKED;
#define AUDIO_V2_SIZEOF_AC_FEATURE_UNIT_DESC(ch) (6 + (ch + 1) * 4)
#define AUDIO_V2_SIZEOF_AC_FEATURE_UNIT_DESC(bNrChannels) (6 + ((bNrChannels) + 1) * 4)
struct audio_v2_cs_if_as_general_descriptor {
uint8_t bLength;
@@ -1124,7 +1111,7 @@ struct audio_v2_control_range3_param_block {
WBVAL(wTotalLength), /* wTotalLength */ \
bmControls /* bmControls */ \
#define AUDIO_V2_AC_DESCRIPTOR_INIT_LEN (0x08 + 0x09 + 0x09)
#define AUDIO_V2_AC_DESCRIPTOR_LEN (0x08 + 0x09 + 0x09)
#define AUDIO_V2_AC_CLOCK_SOURCE_DESCRIPTOR_INIT(bClockID, bmAttributes, bmControls) \
0x08, /* bLength */ \
@@ -1262,7 +1249,7 @@ struct audio_v2_control_range3_param_block {
0x00, /* wLockDelay */ \
0x00
#define AUDIO_V2_AS_ALTSETTING0_DESCRIPTOR_INIT(bInterfaceNumber) \
#define AUDIO_V2_AS_ALTSETTING0_DESCRIPTOR_INIT(bInterfaceNumber) \
0x09, /* bLength */ \
USB_DESCRIPTOR_TYPE_INTERFACE, /* bDescriptorType */ \
bInterfaceNumber, /* bInterfaceNumber */ \
@@ -1331,9 +1318,10 @@ struct audio_v2_control_range3_param_block {
// clang-format on
#define AUDIO_V2_AS_DESCRIPTOR_INIT_LEN (0x09 + 0x09 + 0x10 + 0x06 + 0x07 + 0x08)
#define AUDIO_V2_AS_ALTSETTING_DESCRIPTOR_INIT_LEN (0x09 + 0x10 + 0x06 + 0x07 + 0x08)
#define AUDIO_V2_AS_FEEDBACK_DESCRIPTOR_INIT_LEN (0x09 + 0x09 + 0x10 + 0x06 + 0x07 + 0x08 + 0x07)
#define AUDIO_V2_AS_DESCRIPTOR_LEN (0x09 + 0x09 + 0x10 + 0x06 + 0x07 + 0x08)
#define AUDIO_V2_AS_ALTSETTING0_DESCRIPTOR_LEN (0x09)
#define AUDIO_V2_AS_ALTSETTING_DESCRIPTOR_LEN (0x09 + 0x10 + 0x06 + 0x07 + 0x08)
#define AUDIO_V2_AS_FEEDBACK_DESCRIPTOR_LEN (0x09 + 0x09 + 0x10 + 0x06 + 0x07 + 0x08 + 0x07)
#define AUDIO_SAMPLE_FREQ_NUM(num) (uint8_t)(num), (uint8_t)((num >> 8))
#define AUDIO_SAMPLE_FREQ_3B(frq) (uint8_t)(frq), (uint8_t)((frq >> 8)), (uint8_t)((frq >> 16))

View File

@@ -217,12 +217,12 @@
#define CDC_SERIAL_STATE_BREAK (1 << 2) /* state of break detection */
#define CDC_SERIAL_STATE_BREAK_Pos (2)
#define CDC_SERIAL_STATE_BREAK_Msk (1 << CDC_SERIAL_STATE_BREAK_Pos)
#define CDC_SERIAL_STATE_TX_CARRIER (1 << 1) /* state of transmission carrier */
#define CDC_SERIAL_STATE_TX_CARRIER_Pos (1)
#define CDC_SERIAL_STATE_TX_CARRIER_Msk (1 << CDC_SERIAL_STATE_TX_CARRIER_Pos)
#define CDC_SERIAL_STATE_RX_CARRIER (1 << 0) /* state of receiver carrier */
#define CDC_SERIAL_STATE_RX_CARRIER_Pos (0)
#define CDC_SERIAL_STATE_RX_CARRIER_Msk (1 << CDC_SERIAL_STATE_RX_CARRIER_Pos)
#define CDC_SERIAL_STATE_DSR (1 << 1) /* state of transmission carrier */
#define CDC_SERIAL_STATE_DSR_Pos (1)
#define CDC_SERIAL_STATE_DSR_Msk (1 << CDC_SERIAL_STATE_DSR_Pos)
#define CDC_SERIAL_STATE_DCD (1 << 0) /* state of receiver carrier */
#define CDC_SERIAL_STATE_DCD_Pos (0)
#define CDC_SERIAL_STATE_DCD_Msk (1 << CDC_SERIAL_STATE_DCD_Pos)
#define CDC_ECM_XMIT_OK (1 << 0)
#define CDC_ECM_RVC_OK (1 << 1)
@@ -551,10 +551,9 @@ struct cdc_ncm_ndp16 {
#define DBVAL_BE(x) ((x >> 24) & 0xFF), ((x >> 16) & 0xFF), ((x >> 8) & 0xFF), (x & 0xFF)
/*Length of template descriptor: 71 bytes*/
#define CDC_ECM_DESCRIPTOR_LEN (8 + 9 + 5 + 5 + 13 + 7 + 9 + 7 + 7)
#define CDC_ECM_DESCRIPTOR_LEN (8 + 9 + 5 + 5 + 13 + 7 + 9 + 7 + 7)
// clang-format off
#define CDC_ECM_DESCRIPTOR_INIT(bFirstInterface, int_ep, out_ep, in_ep, wMaxPacketSize, \
eth_statistics, wMaxSegmentSize, wNumberMCFilters, bNumberPowerFilters, str_idx) \
#define CDC_ECM_DESCRIPTOR_INIT(bFirstInterface, int_ep, out_ep, in_ep, wMaxPacketSize, str_idx) \
/* Interface Associate */ \
0x08, /* bLength */ \
USB_DESCRIPTOR_TYPE_INTERFACE_ASSOCIATION, /* bDescriptorType */ \
@@ -587,10 +586,10 @@ eth_statistics, wMaxSegmentSize, wNumberMCFilters, bNumberPowerFilters, str_idx)
CDC_CS_INTERFACE, /* bDescriptorType: CS_INTERFACE */\
CDC_FUNC_DESC_ETHERNET_NETWORKING, /* Ethernet Networking functional descriptor subtype */\
str_idx, /* Device's MAC string index */\
DBVAL_BE(eth_statistics), /* Ethernet statistics (bitmap) */\
WBVAL(wMaxSegmentSize),/* wMaxSegmentSize: Ethernet Maximum Segment size, typically 1514 bytes */\
WBVAL(wNumberMCFilters), /* wNumberMCFilters: the number of multicast filters */\
bNumberPowerFilters, /* bNumberPowerFilters: the number of wakeup power filters */\
DBVAL_BE(0x00000000), /* Ethernet statistics (bitmap) */\
WBVAL(1514), /* wMaxSegmentSize: Ethernet Maximum Segment size, typically 1514 bytes */\
WBVAL(0), /* wNumberMCFilters: the number of multicast filters */ \
0, /* bNumberPowerFilters: the number of wakeup power filters */ \
0x07, /* bLength */ \
USB_DESCRIPTOR_TYPE_ENDPOINT, /* bDescriptorType */ \
int_ep, /* bEndpointAddress */ \
@@ -621,10 +620,9 @@ eth_statistics, wMaxSegmentSize, wNumberMCFilters, bNumberPowerFilters, str_idx)
// clang-format on
/*Length of template descriptor: 77 bytes*/
#define CDC_NCM_DESCRIPTOR_LEN (8 + 9 + 5 + 5 + 13 + 6 + 7 + 9 + 7 + 7)
#define CDC_NCM_DESCRIPTOR_LEN (8 + 9 + 5 + 5 + 13 + 6 + 7 + 9 + 7 + 7)
// clang-format off
#define CDC_NCM_DESCRIPTOR_INIT(bFirstInterface, int_ep, out_ep, in_ep, wMaxPacketSize, \
eth_statistics, wMaxSegmentSize, wNumberMCFilters, bNumberPowerFilters, str_idx) \
#define CDC_NCM_DESCRIPTOR_INIT(bFirstInterface, int_ep, out_ep, in_ep, wMaxPacketSize, str_idx) \
/* Interface Associate */ \
0x08, /* bLength */ \
USB_DESCRIPTOR_TYPE_INTERFACE_ASSOCIATION, /* bDescriptorType */ \
@@ -657,10 +655,10 @@ eth_statistics, wMaxSegmentSize, wNumberMCFilters, bNumberPowerFilters, str_idx)
CDC_CS_INTERFACE, /* bDescriptorType: CS_INTERFACE */\
CDC_FUNC_DESC_ETHERNET_NETWORKING, /* Ethernet Networking functional descriptor subtype */\
str_idx, /* Device's MAC string index */\
DBVAL_BE(eth_statistics), /* Ethernet statistics (bitmap) */\
WBVAL(wMaxPacketSize),/* wMaxSegmentSize: Ethernet Maximum Segment size, typically 1514 bytes */\
WBVAL(wNumberMCFilters), /* wNumberMCFilters: the number of multicast filters */\
bNumberPowerFilters, /* bNumberPowerFilters: the number of wakeup power filters */\
DBVAL_BE(0x00000000), /* Ethernet statistics (bitmap) */\
WBVAL(1514), /* wMaxSegmentSize: Ethernet Maximum Segment size, typically 1514 bytes */\
WBVAL(0), /* wNumberMCFilters: the number of multicast filters */ \
0, /* bNumberPowerFilters: the number of wakeup power filters */ \
0x06, \
CDC_CS_INTERFACE, \
CDC_FUNC_DESC_NCM, \

View File

@@ -1,285 +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_class_info = {
.match_flags = USB_CLASS_MATCH_INTF_CLASS | USB_CLASS_MATCH_INTF_SUBCLASS,
.bInterfaceClass = USB_DEVICE_CLASS_CDC,
.bInterfaceSubClass = CDC_ABSTRACT_CONTROL_MODEL,
.bInterfaceProtocol = 0x00,
.id_table = NULL,
.class_driver = &cdc_acm_class_driver
};
CLASS_INFO_DEFINE const struct usbh_class_info cdc_data_class_info = {
.match_flags = USB_CLASS_MATCH_INTF_CLASS,
.bInterfaceClass = USB_DEVICE_CLASS_CDC_DATA,
.bInterfaceSubClass = 0x00,
.bInterfaceProtocol = 0x00,
.id_table = NULL,
.class_driver = &cdc_data_class_driver
};

View File

@@ -1,50 +0,0 @@
/*
* Copyright (c) 2022, sakumisu
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef USBH_CDC_ACM_H
#define USBH_CDC_ACM_H
#include "usb_cdc.h"
struct usbh_cdc_acm {
struct usbh_hubport *hport;
struct usb_endpoint_descriptor *bulkin; /* Bulk IN endpoint */
struct usb_endpoint_descriptor *bulkout; /* Bulk OUT endpoint */
#ifdef CONFIG_USBHOST_CDC_ACM_NOTIFY
struct usb_endpoint_descriptor *intin; /* INTR IN endpoint (optional) */
#endif
struct usbh_urb bulkout_urb;
struct usbh_urb bulkin_urb;
#ifdef CONFIG_USBHOST_CDC_ACM_NOTIFY
struct usbh_urb intin_urb;
#endif
struct cdc_line_coding linecoding;
uint8_t intf;
uint8_t minor;
void *user_data;
};
#ifdef __cplusplus
extern "C" {
#endif
int usbh_cdc_acm_set_line_coding(struct usbh_cdc_acm *cdc_acm_class, struct cdc_line_coding *line_coding);
int usbh_cdc_acm_get_line_coding(struct usbh_cdc_acm *cdc_acm_class, struct cdc_line_coding *line_coding);
int usbh_cdc_acm_set_line_state(struct usbh_cdc_acm *cdc_acm_class, bool dtr, bool rts);
int usbh_cdc_acm_bulk_in_transfer(struct usbh_cdc_acm *cdc_acm_class, uint8_t *buffer, uint32_t buflen, uint32_t timeout);
int usbh_cdc_acm_bulk_out_transfer(struct usbh_cdc_acm *cdc_acm_class, uint8_t *buffer, uint32_t buflen, uint32_t timeout);
void usbh_cdc_acm_run(struct usbh_cdc_acm *cdc_acm_class);
void usbh_cdc_acm_stop(struct usbh_cdc_acm *cdc_acm_class);
#ifdef __cplusplus
}
#endif
#endif /* USBH_CDC_ACM_H */

View File

@@ -306,16 +306,6 @@ int usbh_cdc_ecm_eth_output(uint32_t buflen)
return usbh_submit_urb(&g_cdc_ecm_class.bulkout_urb);
}
__WEAK void usbh_cdc_ecm_run(struct usbh_cdc_ecm *cdc_ecm_class)
{
(void)cdc_ecm_class;
}
__WEAK void usbh_cdc_ecm_stop(struct usbh_cdc_ecm *cdc_ecm_class)
{
(void)cdc_ecm_class;
}
const struct usbh_class_driver cdc_ecm_class_driver = {
.driver_name = "cdc_ecm",
.connect = usbh_cdc_ecm_connect,

View File

@@ -386,16 +386,6 @@ int usbh_cdc_ncm_eth_output(uint32_t buflen)
return usbh_submit_urb(&g_cdc_ncm_class.bulkout_urb);
}
__WEAK void usbh_cdc_ncm_run(struct usbh_cdc_ncm *cdc_ncm_class)
{
(void)cdc_ncm_class;
}
__WEAK void usbh_cdc_ncm_stop(struct usbh_cdc_ncm *cdc_ncm_class)
{
(void)cdc_ncm_class;
}
const struct usbh_class_driver cdc_ncm_class_driver = {
.driver_name = "cdc_ncm",
.connect = usbh_cdc_ncm_connect,

View File

@@ -106,7 +106,6 @@ static void dfu_request_upload(struct usb_setup_packet *setup, uint8_t **data, u
{
struct usb_setup_packet *req = setup;
uint32_t addr;
uint8_t *phaddr;
/* Data setup request */
if (req->wLength > 0U) {
if ((g_usbd_dfu.dev_state == DFU_STATE_DFU_IDLE) || (g_usbd_dfu.dev_state == DFU_STATE_DFU_UPLOAD_IDLE)) {
@@ -143,7 +142,7 @@ static void dfu_request_upload(struct usb_setup_packet *setup, uint8_t **data, u
addr = ((g_usbd_dfu.wblock_num - 2U) * USBD_DFU_XFER_SIZE) + g_usbd_dfu.data_ptr;
/* Return the physical address where data are stored */
phaddr = dfu_read_flash((uint8_t *)addr, g_usbd_dfu.buffer.d8, g_usbd_dfu.wlength);
dfu_read_flash((uint8_t *)addr, g_usbd_dfu.buffer.d8, g_usbd_dfu.wlength);
/* Send the status data over EP0 */
memcpy(*data, g_usbd_dfu.buffer.d8, g_usbd_dfu.wlength);

View File

@@ -0,0 +1,224 @@
/*
* Copyright (c) 2026, sakumisu
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef USB_GAMEPAD_H
#define USB_GAMEPAD_H
#include "usb_hid.h"
/*
* GAMEPAD BUTTON LAYOUT
*
* ____________________________ __
* / [__L2__] [__R2__] \ |
* / [__ L1 __] [__ R1 __] \ | Triggers
* __/________________________________\__ __|
* / _ \ |
* / /\ __ (B4) \ |
* / || __ |A1| __ _ _ \ | Main Pad
* | <===DP===> |S1| |S2| (B3) -|- (B2)| |
* \ || ¯¯ ¯¯ _ / |
* /\ \/ / \ / \ (B1) /\ __|
* / \________ | LS | ____ | RS | _______/ \ |
* | / \ \___/ / \ \___/ / \ | | Sticks
* | / \_____/ \_____/ \ | __|
* | / L3 R3 \ |
* \_____/ \_____/
*
* |________|______| |______|___________|
* D-Pad Left Right Face
* Stick Stick Buttons
*
* Extended: A2=Touchpad/Capture A3=Mute L4/R4=Paddles
*/
// W3C Gamepad API standard button order
// Bit position = W3C button index (trivial conversion: 1 << index)
//
// Gamepad XInput Switch PS3/4/5 DInput
// ------ ------ ------ ------- ------
// Face buttons (right cluster)
#define USB_GAMEPAD_BUTTON_B1 (1 << 0) // A B Cross 2
#define USB_GAMEPAD_BUTTON_B2 (1 << 1) // B A Circle 3
#define USB_GAMEPAD_BUTTON_B3 (1 << 2) // X Y Square 1
#define USB_GAMEPAD_BUTTON_B4 (1 << 3) // Y X Triangle 4
// Shoulder buttons
#define USB_GAMEPAD_BUTTON_L1 (1 << 4) // LB L L1 5
#define USB_GAMEPAD_BUTTON_R1 (1 << 5) // RB R R1 6
#define USB_GAMEPAD_BUTTON_L2 (1 << 6) // LT ZL L2 7
#define USB_GAMEPAD_BUTTON_R2 (1 << 7) // RT ZR R2 8
// Center cluster
#define USB_GAMEPAD_BUTTON_S1 (1 << 8) // Back - Select 9
#define USB_GAMEPAD_BUTTON_S2 (1 << 9) // Start + Start 10
// Stick clicks
#define USB_GAMEPAD_BUTTON_L3 (1 << 10) // LS LS L3 11
#define USB_GAMEPAD_BUTTON_R3 (1 << 11) // RS RS R3 12
// D-pad
#define USB_GAMEPAD_BUTTON_DU (1 << 12) // D-Up D-Up D-Up Hat
#define USB_GAMEPAD_BUTTON_DD (1 << 13) // D-Down D-Down D-Down Hat
#define USB_GAMEPAD_BUTTON_DL (1 << 14) // D-Left D-Left D-Left Hat
#define USB_GAMEPAD_BUTTON_DR (1 << 15) // D-Right D-Right D-Right Hat
// Auxiliary
#define USB_GAMEPAD_BUTTON_A1 (1 << 16) // Guide Home PS 13
#define USB_GAMEPAD_BUTTON_A2 (1 << 17) // - Capture Touchpad 14
#define USB_GAMEPAD_BUTTON_A3 (1 << 18) // - - Mute -
#define USB_GAMEPAD_BUTTON_A4 (1 << 19) // - - - -
// Paddles (extended)
#define USB_GAMEPAD_BUTTON_L4 (1 << 20) // P1 - - -
#define USB_GAMEPAD_BUTTON_R4 (1 << 21) // P2 - - -
#define XINPUT_VID 0x045E // Microsoft
#define XINPUT_PID 0x028E // Xbox 360 Controller
#define XINPUT_BCD_DEVICE 0x0114 // v1.14
/* XInput (Xbox 360) USB */
// XInput Interface Class/Subclass/Protocol
#define XINPUT_INTERFACE_CLASS 0xFF
#define XINPUT_INTERFACE_SUBCLASS 0x5D
#define XINPUT_INTERFACE_PROTOCOL 0x01
#define XINPUT_BUTTON_MASK_UP (1U << 0)
#define XINPUT_BUTTON_MASK_DOWN (1U << 1)
#define XINPUT_BUTTON_MASK_LEFT (1U << 2)
#define XINPUT_BUTTON_MASK_RIGHT (1U << 3)
#define XINPUT_BUTTON_MASK_START (1U << 4)
#define XINPUT_BUTTON_MASK_BACK (1U << 5)
#define XINPUT_BUTTON_MASK_L3 (1U << 6)
#define XINPUT_BUTTON_MASK_R3 (1U << 7)
#define XINPUT_BUTTON_MASK_LB (1U << 8)
#define XINPUT_BUTTON_MASK_RB (1U << 9)
#define XINPUT_BUTTON_MASK_GUIDE (1U << 10)
//#define XINPUT_BUTTON_MASK_UNUSED (1U << 11)
#define XINPUT_BUTTON_MASK_A (1U << 12)
#define XINPUT_BUTTON_MASK_B (1U << 13)
#define XINPUT_BUTTON_MASK_X (1U << 14)
#define XINPUT_BUTTON_MASK_Y (1U << 15)
// LED patterns for report_id 0x01
#define XINPUT_LED_OFF 0x00
#define XINPUT_LED_BLINK 0x01
#define XINPUT_LED_FLASH_1 0x02
#define XINPUT_LED_FLASH_2 0x03
#define XINPUT_LED_FLASH_3 0x04
#define XINPUT_LED_FLASH_4 0x05
#define XINPUT_LED_ON_1 0x06
#define XINPUT_LED_ON_2 0x07
#define XINPUT_LED_ON_3 0x08
#define XINPUT_LED_ON_4 0x09
#define XINPUT_LED_ROTATE 0x0A
#define XINPUT_LED_BLINK_SLOW 0x0B
#define XINPUT_LED_BLINK_SLOW_1 0x0C
#define XINPUT_LED_BLINK_SLOW_2 0x0D
struct xinput_in_report {
uint8_t report_id; /* Always 0x00 */
uint8_t report_size; /* Always 0x14 (20) */
uint16_t buttons; /* DPAD, Start, Back, L3, R3, LB, RB, Guide, A, B, X, Y */
uint8_t lt; /* Left trigger (0-255) */
uint8_t rt; /* Right trigger (0-255) */
int16_t lx; /* Left stick X (-32768 to 32767) */
int16_t ly; /* Left stick Y (-32768 to 32767) */
int16_t rx; /* Right stick X (-32768 to 32767) */
int16_t ry; /* Right stick Y (-32768 to 32767) */
uint8_t reserved[6]; /* Reserved/padding */
} __PACKED;
struct xinput_out_report {
uint8_t report_id; // 0x00 = rumble, 0x01 = LED
uint8_t report_size; // 0x08
uint8_t led; // LED pattern (0x00 for rumble)
uint8_t rumble_l; // Left motor (large, 0-255)
uint8_t rumble_r; // Right motor (small, 0-255)
uint8_t reserved[3]; // Padding
} __PACKED;
// clang-format off
#define XINPUT_DESCRIPTOR_LEN (9 + 16 + 7 + 7)
#define XINPUT_DESCRIPTOR_INIT(bInterfaceNumber, out_ep, in_ep) \
USB_INTERFACE_DESCRIPTOR_INIT(bInterfaceNumber, 0x00, 0x02, 0xff, 0x5d, 0x01, 0x00), /* XInput proprietary descriptor (0x21) */ \
16, 0x21, 0x00, 0x01, 0x01, 0x24, in_ep, 0x14, 0x03, 0x00, 0x03, 0x13, out_ep, 0x00, 0x03, 0x00, \
USB_ENDPOINT_DESCRIPTOR_INIT(in_ep, 0x03, 32, 0x01), \
USB_ENDPOINT_DESCRIPTOR_INIT(out_ep, 0x03, 32, 0x08)
// clang-format on
#define SWITCH_VID 0x0F0D // 0x057E Nintendo Pro Controller
#define SWITCH_PID 0x0092 // 0x2009
#define SWITCH_BCD_DEVICE 0x0100 // v1.00
// Button masks (16-bit)
#define SWITCH_MASK_Y (1U << 0)
#define SWITCH_MASK_B (1U << 1)
#define SWITCH_MASK_A (1U << 2)
#define SWITCH_MASK_X (1U << 3)
#define SWITCH_MASK_L (1U << 4)
#define SWITCH_MASK_R (1U << 5)
#define SWITCH_MASK_ZL (1U << 6)
#define SWITCH_MASK_ZR (1U << 7)
#define SWITCH_MASK_MINUS (1U << 8)
#define SWITCH_MASK_PLUS (1U << 9)
#define SWITCH_MASK_L3 (1U << 10)
#define SWITCH_MASK_R3 (1U << 11)
#define SWITCH_MASK_HOME (1U << 12)
#define SWITCH_MASK_CAPTURE (1U << 13)
// D-pad / Hat switch values
#define SWITCH_HAT_UP 0x00
#define SWITCH_HAT_UP_RIGHT 0x01
#define SWITCH_HAT_RIGHT 0x02
#define SWITCH_HAT_DOWN_RIGHT 0x03
#define SWITCH_HAT_DOWN 0x04
#define SWITCH_HAT_DOWN_LEFT 0x05
#define SWITCH_HAT_LEFT 0x06
#define SWITCH_HAT_UP_LEFT 0x07
#define SWITCH_HAT_CENTER 0x08
// Analog stick range
#define SWITCH_JOYSTICK_MIN 0x00
#define SWITCH_JOYSTICK_MID 0x80
#define SWITCH_JOYSTICK_MAX 0xFF
struct switch_in_report {
uint16_t buttons; // 16 button bits
uint8_t hat; // D-pad (hat switch, 0-8)
uint8_t lx; // Left stick X (0-255, 128 = center)
uint8_t ly; // Left stick Y (0-255, 128 = center)
uint8_t rx; // Right stick X (0-255, 128 = center)
uint8_t ry; // Right stick Y (0-255, 128 = center)
uint8_t vendor; // Vendor-specific byte
} __PACKED;
struct switch_out_report {
uint8_t data[8]; // Vendor-specific rumble data
} __PACKED;
#define HID_SWITCH_REPORT_DESC_SIZE 86
// clang-format off
#define SWITCH_DESCRIPTOR_LEN HID_CUSTOM_INOUT_DESCRIPTOR_LEN
#define SWITCH_DESCRIPTOR_INIT(bInterfaceNumber, out_ep, in_ep) \
HID_CUSTOM_INOUT_DESCRIPTOR_INIT(bInterfaceNumber, 0x00, HID_SWITCH_REPORT_DESC_SIZE, out_ep, in_ep, 64, 0x01)
// clang-format on
struct usb_gamepad_report {
uint32_t buttons;
uint8_t lt;
uint8_t rt;
uint8_t lx;
uint8_t ly;
uint8_t rx;
uint8_t ry;
};
#endif /* USB_GAMEPAD_H */

View File

@@ -0,0 +1,209 @@
/*
* Copyright (c) 2026, sakumisu
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "usbd_core.h"
#include "usbd_hid.h"
#include "usbd_gamepad.h"
USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t gamepad_report_buffer[64];
static int xinput_vendor_class_request_handler(uint8_t busid, struct usb_setup_packet *setup, uint8_t **data, uint32_t *len)
{
struct xinput_in_report xinput_report;
memset(&xinput_report, 0, sizeof(xinput_report));
xinput_report.report_size = 20;
memcpy(*data, &xinput_report, sizeof(xinput_report));
*len = sizeof(xinput_report);
return 0;
}
int usbd_gamepad_xinput_send_report(uint8_t ep, struct usb_gamepad_report *report)
{
struct xinput_in_report *xinput_report;
xinput_report = (struct xinput_in_report *)gamepad_report_buffer;
memset(xinput_report, 0, sizeof(xinput_report));
xinput_report->report_size = 20;
if (report->buttons & USB_GAMEPAD_BUTTON_DU)
xinput_report->buttons |= XINPUT_BUTTON_MASK_UP;
if (report->buttons & USB_GAMEPAD_BUTTON_DD)
xinput_report->buttons |= XINPUT_BUTTON_MASK_DOWN;
if (report->buttons & USB_GAMEPAD_BUTTON_DL)
xinput_report->buttons |= XINPUT_BUTTON_MASK_LEFT;
if (report->buttons & USB_GAMEPAD_BUTTON_DR)
xinput_report->buttons |= XINPUT_BUTTON_MASK_RIGHT;
if (report->buttons & USB_GAMEPAD_BUTTON_S2)
xinput_report->buttons |= XINPUT_BUTTON_MASK_START;
if (report->buttons & USB_GAMEPAD_BUTTON_S1)
xinput_report->buttons |= XINPUT_BUTTON_MASK_BACK;
if (report->buttons & USB_GAMEPAD_BUTTON_L3)
xinput_report->buttons |= XINPUT_BUTTON_MASK_L3;
if (report->buttons & USB_GAMEPAD_BUTTON_R3)
xinput_report->buttons |= XINPUT_BUTTON_MASK_R3;
if (report->buttons & USB_GAMEPAD_BUTTON_L1)
xinput_report->buttons |= XINPUT_BUTTON_MASK_LB;
if (report->buttons & USB_GAMEPAD_BUTTON_R1)
xinput_report->buttons |= XINPUT_BUTTON_MASK_RB;
if (report->buttons & USB_GAMEPAD_BUTTON_A1)
xinput_report->buttons |= XINPUT_BUTTON_MASK_GUIDE;
if (report->buttons & USB_GAMEPAD_BUTTON_B1)
xinput_report->buttons |= XINPUT_BUTTON_MASK_A;
if (report->buttons & USB_GAMEPAD_BUTTON_B2)
xinput_report->buttons |= XINPUT_BUTTON_MASK_B;
if (report->buttons & USB_GAMEPAD_BUTTON_B3)
xinput_report->buttons |= XINPUT_BUTTON_MASK_X;
if (report->buttons & USB_GAMEPAD_BUTTON_B4)
xinput_report->buttons |= XINPUT_BUTTON_MASK_Y;
// Analog triggers (0-255), fall back to digital if analog is 0 but button pressed
xinput_report->lt = report->lt;
xinput_report->rt = report->rt;
if (xinput_report->lt == 0 && (report->buttons & USB_GAMEPAD_BUTTON_L2))
xinput_report->lt = 0xFF;
if (xinput_report->rt == 0 && (report->buttons & USB_GAMEPAD_BUTTON_R2))
xinput_report->rt = 0xFF;
return usbd_ep_start_write(0, ep, gamepad_report_buffer, sizeof(struct xinput_in_report));
}
// Convert gamepad dpad mask to switch hat value
static uint8_t convert_dpad_to_switch_hat(uint32_t buttons)
{
// Joypad uses active-high (1 = pressed)
uint8_t up = (buttons & USB_GAMEPAD_BUTTON_DU) ? 1 : 0;
uint8_t down = (buttons & USB_GAMEPAD_BUTTON_DD) ? 1 : 0;
uint8_t left = (buttons & USB_GAMEPAD_BUTTON_DL) ? 1 : 0;
uint8_t right = (buttons & USB_GAMEPAD_BUTTON_DR) ? 1 : 0;
if (up && right)
return SWITCH_HAT_UP_RIGHT;
if (up && left)
return SWITCH_HAT_UP_LEFT;
if (down && right)
return SWITCH_HAT_DOWN_RIGHT;
if (down && left)
return SWITCH_HAT_DOWN_LEFT;
if (up)
return SWITCH_HAT_UP;
if (down)
return SWITCH_HAT_DOWN;
if (left)
return SWITCH_HAT_LEFT;
if (right)
return SWITCH_HAT_RIGHT;
return SWITCH_HAT_CENTER;
}
int usbd_gamepad_switch_send_report(uint8_t ep, struct usb_gamepad_report *report)
{
struct switch_in_report *switch_report;
switch_report = (struct switch_in_report *)gamepad_report_buffer;
memset(switch_report, 0, sizeof(switch_report));
if (report->buttons & USB_GAMEPAD_BUTTON_S1)
switch_report->buttons |= SWITCH_MASK_MINUS;
if (report->buttons & USB_GAMEPAD_BUTTON_S2)
switch_report->buttons |= SWITCH_MASK_PLUS;
if (report->buttons & USB_GAMEPAD_BUTTON_L1)
switch_report->buttons |= SWITCH_MASK_L;
if (report->buttons & USB_GAMEPAD_BUTTON_R1)
switch_report->buttons |= SWITCH_MASK_R;
if (report->buttons & USB_GAMEPAD_BUTTON_L2)
switch_report->buttons |= SWITCH_MASK_ZL;
if (report->buttons & USB_GAMEPAD_BUTTON_R2)
switch_report->buttons |= SWITCH_MASK_ZR;
if (report->buttons & USB_GAMEPAD_BUTTON_L3)
switch_report->buttons |= SWITCH_MASK_L3;
if (report->buttons & USB_GAMEPAD_BUTTON_R3)
switch_report->buttons |= SWITCH_MASK_R3;
if (report->buttons & USB_GAMEPAD_BUTTON_A1)
switch_report->buttons |= SWITCH_MASK_HOME;
if (report->buttons & USB_GAMEPAD_BUTTON_A2)
switch_report->buttons |= SWITCH_MASK_CAPTURE;
if (report->buttons & USB_GAMEPAD_BUTTON_B1)
switch_report->buttons |= SWITCH_MASK_B;
if (report->buttons & USB_GAMEPAD_BUTTON_B2)
switch_report->buttons |= SWITCH_MASK_A;
if (report->buttons & USB_GAMEPAD_BUTTON_B3)
switch_report->buttons |= SWITCH_MASK_Y;
if (report->buttons & USB_GAMEPAD_BUTTON_B4)
switch_report->buttons |= SWITCH_MASK_X;
switch_report->hat = convert_dpad_to_switch_hat(report->buttons);
// Analog sticks (HID convention: 0=up, 255=down - no inversion needed)
switch_report->lx = report->lx;
switch_report->ly = report->ly;
switch_report->rx = report->rx;
switch_report->ry = report->ry;
switch_report->vendor = 0;
return usbd_ep_start_write(0, ep, gamepad_report_buffer, sizeof(struct switch_in_report));
}
struct usbd_interface *usbd_gamepad_xinput_init_intf(struct usbd_interface *intf)
{
intf->class_interface_handler = NULL;
intf->class_endpoint_handler = NULL;
intf->vendor_handler = xinput_vendor_class_request_handler;
intf->notify_handler = NULL;
return intf;
}
static const uint8_t hid_switch_report_desc[HID_SWITCH_REPORT_DESC_SIZE] = {
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
0x09, 0x05, // Usage (Game Pad)
0xA1, 0x01, // Collection (Application)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x01, // Logical Maximum (1)
0x35, 0x00, // Physical Minimum (0)
0x45, 0x01, // Physical Maximum (1)
0x75, 0x01, // Report Size (1)
0x95, 0x10, // Report Count (16)
0x05, 0x09, // Usage Page (Button)
0x19, 0x01, // Usage Minimum (Button 1)
0x29, 0x10, // Usage Maximum (Button 16)
0x81, 0x02, // Input (Data,Var,Abs)
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
0x25, 0x07, // Logical Maximum (7)
0x46, 0x3B, 0x01, // Physical Maximum (315)
0x75, 0x04, // Report Size (4)
0x95, 0x01, // Report Count (1)
0x65, 0x14, // Unit (Eng Rot:Angular Pos)
0x09, 0x39, // Usage (Hat switch)
0x81, 0x42, // Input (Data,Var,Abs,Null)
0x65, 0x00, // Unit (None)
0x95, 0x01, // Report Count (1)
0x81, 0x01, // Input (Const) - 4-bit padding
0x26, 0xFF, 0x00, // Logical Maximum (255)
0x46, 0xFF, 0x00, // Physical Maximum (255)
0x09, 0x30, // Usage (X) - Left Stick X
0x09, 0x31, // Usage (Y) - Left Stick Y
0x09, 0x32, // Usage (Z) - Right Stick X
0x09, 0x35, // Usage (Rz) - Right Stick Y
0x75, 0x08, // Report Size (8)
0x95, 0x04, // Report Count (4)
0x81, 0x02, // Input (Data,Var,Abs)
0x06, 0x00, 0xFF, // Usage Page (Vendor Defined)
0x09, 0x20, // Usage (0x20)
0x95, 0x01, // Report Count (1)
0x81, 0x02, // Input (Data,Var,Abs) - Vendor byte
0x0A, 0x21, 0x26, // Usage (0x2621)
0x95, 0x08, // Report Count (8)
0x91, 0x02, // Output (Data,Var,Abs) - Rumble
0xC0, // End Collection
};
struct usbd_interface *usbd_gamepad_switch_init_intf(struct usbd_interface *intf)
{
return usbd_hid_init_intf(0, intf, hid_switch_report_desc, HID_SWITCH_REPORT_DESC_SIZE);
}

View File

@@ -0,0 +1,22 @@
/*
* Copyright (c) 2026, sakumisu
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef USBD_GAMEPAD_H
#define USBD_GAMEPAD_H
#include "usb_gamepad.h"
#define USBD_GAMEPAD_MODE_XINPUT 0
#define USBD_GAMEPAD_MODE_SWITCH 1
#define USBD_GAMEPAD_MODE_XBOXONE 2
#define USBD_GAMEPAD_MODE_PS4 3
struct usbd_interface *usbd_gamepad_xinput_init_intf(struct usbd_interface *intf);
struct usbd_interface *usbd_gamepad_switch_init_intf(struct usbd_interface *intf);
int usbd_gamepad_xinput_send_report(uint8_t ep, struct usb_gamepad_report *report);
int usbd_gamepad_switch_send_report(uint8_t ep, struct usb_gamepad_report *report);
#endif /* USBD_GAMEPAD_H */

View File

@@ -35,7 +35,7 @@
#define HID_REPORT_OUTPUT 0x02
#define HID_REPORT_FEATURE 0x03
/* HID Descriptor ***********************************************************/
/* HID Descriptor */
#define HID_COUNTRY_NONE 0x00 /* Not Supported */
#define HID_COUNTRY_ARABIC 0x01 /* Arabic */
@@ -74,99 +74,114 @@
#define HID_COUNTRY_YUGOSLAVIA 0x34 /* Yugoslavia */
#define HID_COUNTRY_TURKISHF 0x35 /* Turkish-F */
/* HID report items */
#define HID_REPORT_ITEM_SIZE_MASK 0x03
#define HID_REPORT_ITEM_SIZE_0 0x00 /* No data follows */
#define HID_REPORT_ITEM_SIZE_1 0x01 /* 1 byte of data follows */
#define HID_REPORT_ITEM_SIZE_2 0x02 /* 2 bytes of data follow */
#define HID_REPORT_ITEM_SIZE_4 0x03 /* 4 bytes of data follow */
#define HID_REPORT_ITEM_TYPE_MASK 0x0c
#define HID_REPORT_ITEM_TYPE_MAIN 0x00
#define HID_REPORT_ITEM_TYPE_GLOBAL 0x04
#define HID_REPORT_ITEM_TYPE_LOCAL 0x08
#define HID_REPORT_ITEM_TAG_MASK 0xf0
/* HID report See specification at
* https://www.usb.org/sites/default/files/hid1_11.pdf
* https://www.usb.org/sites/default/files/hut1_22.pdf
*/
#define HID_SIZE_MASK (0x3 << 0)
#define HID_TYPE_MASK (0x3 << 2)
#define HID_TAG_MASK (0xF << 4)
#define HID_ITEMTYPE_MAIN (0x0 << 2)
#define HID_ITEMTYPE_GLOBAL (0x1 << 2)
#define HID_ITEMTYPE_LOCAL (0x2 << 2)
#define HID_ITEMTYPE_LONG (0x3 << 2)
/* Main Items (HID 6.2.2.4) */
#define HID_MAIN_ITEM_CONSTANT (1 << 0) /* Constant(1) vs Data(0) */
#define HID_MAIN_ITEM_VARIABLE (1 << 1) /* Variable(1) vs Array(0) */
#define HID_MAIN_ITEM_RELATIVE (1 << 2) /* Relative(1) vs Absolute(0) */
#define HID_MAIN_ITEM_WRAP (1 << 3) /* Wrap(1) vs No Wrap(0) */
#define HID_MAIN_ITEM_NONLINEAR (1 << 4) /* Non Linear(1) vs Linear(0) */
#define HID_MAIN_ITEM_NOPREFERRED (1 << 5) /* No Preferred (1) vs Preferred State(0) */
#define HID_MAIN_ITEM_NULLSTATE (1 << 6) /* Null state(1) vs No Null position(0) */
#define HID_MAIN_ITEM_VOLATILE (1 << 7) /* Volatile(1) vs Non volatile(0) */
#define HID_MAIN_ITEM_BUFFEREDBYTES (1 << 8) /* Buffered Bytes(1) vs Bit Field(0) */
#define HID_MAINITEM_TAG_INPUT (0x08 << 4)
#define HID_MAINITEM_TAG_OUTPUT (0x09 << 4)
#define HID_MAINITEM_TAG_COLLECTION (0x0a << 4)
#define HID_MAINITEM_TAG_FEATURE (0x0b << 4)
#define HID_MAINITEM_TAG_ENDCOLLECTION (0x0c << 4)
#define HID_MAIN_ITEM_SIZE(pfx) ((pfx)&HID_REPORT_ITEM_SIZE_MASK)
#define HID_MAIN_ITEM_INPUT_PREFIX 0x80
#define HID_MAIN_ITEM_INPUT_CONSTANT HID_MAIN_ITEM_CONSTANT
#define HID_MAIN_ITEM_INPUT_VARIABLE HID_MAIN_ITEM_VARIABLE
#define HID_MAIN_ITEM_INPUT_RELATIVE HID_MAIN_ITEM_RELATIVE
#define HID_MAIN_ITEM_INPUT_WRAP HID_MAIN_ITEM_WRAP
#define HID_MAIN_ITEM_INPUT_NONLINEAR HID_MAIN_ITEM_NONLINEAR
#define HID_MAIN_ITEM_INPUT_NOPREFERRED HID_MAIN_ITEM_NOPREFERRED
#define HID_MAIN_ITEM_INPUT_NULLSTATE HID_MAIN_ITEM_NULLSTATE
#define HID_MAIN_ITEM_INPUT_BUFFEREDBYTES HID_MAIN_ITEM_BUFFEREDBYTES
#define HID_MAINITEM_CONSTANT (1 << 0) /* Constant(1) vs Data(0) */
#define HID_MAINITEM_VARIABLE (1 << 1) /* Variable(1) vs Array(0) */
#define HID_MAINITEM_RELATIVE (1 << 2) /* Relative(1) vs Absolute(0) */
#define HID_MAINITEM_WRAP (1 << 3) /* Wrap(1) vs No Wrap(0) */
#define HID_MAINITEM_NONLINEAR (1 << 4) /* Non Linear(1) vs Linear(0) */
#define HID_MAINITEM_NOPREFERRED (1 << 5) /* No Preferred (1) vs Preferred State(0) */
#define HID_MAINITEM_NULLSTATE (1 << 6) /* Null state(1) vs No Null position(0) */
#define HID_MAINITEM_VOLATILE (1 << 7) /* Volatile(1) vs Non volatile(0) */
#define HID_MAINITEM_BUFFEREDBYTES (1 << 8) /* Buffered Bytes(1) vs Bit Field(0) */
#define HID_MAIN_ITEM_OUTPUT_PREFIX 0x90
#define HID_MAIN_ITEM_OUTPUT_CONSTANT HID_MAIN_ITEM_CONSTANT
#define HID_MAIN_ITEM_OUTPUT_VARIABLE HID_MAIN_ITEM_VARIABLE
#define HID_MAIN_ITEM_OUTPUT_RELATIVE HID_MAIN_ITEM_RELATIVE
#define HID_MAIN_ITEM_OUTPUT_WRAP HID_MAIN_ITEM_WRAP
#define HID_MAIN_ITEM_OUTPUT_NONLINEAR HID_MAIN_ITEM_NONLINEAR
#define HID_MAIN_ITEM_OUTPUT_NOPREFERRED HID_MAIN_ITEM_NOPREFERRED
#define HID_MAIN_ITEM_OUTPUT_NULLSTATE HID_MAIN_ITEM_NULLSTATE
#define HID_MAIN_ITEM_OUTPUT_VOLATILE HID_MAIN_ITEM_VOLATILE
#define HID_MAIN_ITEM_OUTPUT_BUFFEREDBYTES HID_MAIN_ITEM_BUFFEREDBYTES
#define HID_MAIN_ITEM_FEATURE_PREFIX 0xb0
#define HID_MAIN_ITEM_FEATURE_CONSTANT HID_MAIN_ITEM_CONSTANT
#define HID_MAIN_ITEM_FEATURE_VARIABLE HID_MAIN_ITEM_VARIABLE
#define HID_MAIN_ITEM_FEATURE_RELATIVE HID_MAIN_ITEM_RELATIVE
#define HID_MAIN_ITEM_FEATURE_WRAP HID_MAIN_ITEM_WRAP
#define HID_MAIN_ITEM_FEATURE_NONLINEAR HID_MAIN_ITEM_NONLINEAR
#define HID_MAIN_ITEM_FEATURE_NOPREFERRED HID_MAIN_ITEM_NOPREFERRED
#define HID_MAIN_ITEM_FEATURE_NULLSTATE HID_MAIN_ITEM_NULLSTATE
#define HID_MAIN_ITEM_FEATURE_VOLATILE HID_MAIN_ITEM_VOLATILE
#define HID_MAIN_ITEM_FEATURE_BUFFEREDBYTES HID_MAIN_ITEM_BUFFEREDBYTES
#define HID_MAIN_ITEM_COLLECTION_PREFIX 0xa0
#define HID_MAIN_ITEM_COLLECTION_PHYSICAL 0x00 /* Physical (group of axes) */
#define HID_MAIN_ITEM_COLLECTION_APPL 0x01 /* Application (mouse, keyboard) */
#define HID_MAIN_ITEM_COLLECTION_LOGICAL 0x02 /* Logical (interrelated data) */
#define HID_MAIN_ITEM_COLLECTION_REPORT 0x03 /* Report */
#define HID_MAIN_ITEM_COLLECTION_ARRAY 0x04 /* Named Array */
#define HID_MAIN_ITEM_COLLECTION_SWITCH 0x05 /* Usage Switch */
#define HID_MAIN_ITEM_COLLECTION_MODIFIER 0x06 /* Usage Modifier */
#define HID_MAIN_ITEM_ENDCOLLECTION_PREFIX 0xc0
#define HID_MAINITEM_COLLECTION_PHYSICAL 0x00 /* Physical (group of axes) */
#define HID_MAINITEM_COLLECTION_APPL 0x01 /* Application (mouse, keyboard) */
#define HID_MAINITEM_COLLECTION_LOGICAL 0x02 /* Logical (interrelated data) */
#define HID_MAINITEM_COLLECTION_REPORT 0x03 /* Report */
#define HID_MAINITEM_COLLECTION_ARRAY 0x04 /* Named Array */
#define HID_MAINITEM_COLLECTION_SWITCH 0x05 /* Usage Switch */
#define HID_MAINITEM_COLLECTION_MODIFIER 0x06 /* Usage Modifier */
/* Global Items (HID 6.2.2.7) */
#define HID_GLOBAL_ITEM_SIZE(pfx) ((pfx)&HID_REPORT_ITEM_SIZE_MASK)
#define HID_GLOBAL_ITEM_USAGEPAGE_PREFIX 0x04 /* Usage Page */
#define HID_GLOBAL_ITEM_LOGICALMIN_PREFIX 0x14 /* Logical Minimum */
#define HID_GLOBAL_ITEM_LOGICALMAX_PREFIX 0x24 /* Logical Maximum */
#define HID_GLOBAL_ITEM_PHYSICALMIN_PREFIX 0x34 /* Physical Minimum */
#define HID_GLOBAL_ITEM_PHYSMICALAX_PREFIX 0x44 /* Physical Maximum */
#define HID_GLOBAL_ITEM_UNITEXP_PREFIX 0x54 /* Unit Exponent */
#define HID_GLOBAL_ITEM_UNIT_PREFIX 0x64 /* Unit */
#define HID_GLOBAL_ITEM_REPORTSIZE_PREFIX 0x74 /* Report Size */
#define HID_GLOBAL_ITEM_REPORTID_PREFIX 0x84 /* Report ID */
#define HID_GLOBAL_ITEM_REPORTCOUNT_PREFIX 0x94 /* Report Count */
#define HID_GLOBAL_ITEM_PUSH_PREFIX 0xa4 /* Push */
#define HID_GLOBAL_ITEM_POP_PREFIX 0xb4 /* Pop */
#define HID_GLOBALITEM_TAG_USAGE_PAGE (0x00 << 4)
#define HID_GLOBALITEM_TAG_LOGICAL_MIN (0x01 << 4)
#define HID_GLOBALITEM_TAG_LOGICAL_MAX (0x02 << 4)
#define HID_GLOBALITEM_TAG_PHYSICAL_MIN (0x03 << 4)
#define HID_GLOBALITEM_TAG_PHYSICAL_MAX (0x04 << 4)
#define HID_GLOBALITEM_TAG_UNIT_EXP (0x05 << 4)
#define HID_GLOBALITEM_TAG_UNIT (0x06 << 4)
#define HID_GLOBALITEM_TAG_REPORT_SIZE (0x07 << 4)
#define HID_GLOBALITEM_TAG_REPORT_ID (0x08 << 4)
#define HID_GLOBALITEM_TAG_REPORT_COUNT (0x09 << 4)
#define HID_GLOBALITEM_TAG_PUSH (0x0a << 4)
#define HID_GLOBALITEM_TAG_POP (0x0b << 4)
/* Local Items (HID 6.2.2.8) */
#define HID_LOCAL_ITEM_SIZE(pfx) ((pfx)&HID_REPORT_ITEM_SIZE_MASK)
#define HID_LOCAL_ITEM_USAGE_PREFIX 0x08 /* Usage */
#define HID_LOCAL_ITEM_USAGEMIN_PREFIX 0x18 /* Usage Minimum */
#define HID_LOCAL_ITEM_USAGEMAX_PREFIX 0x28 /* Usage Maximum */
#define HID_LOCAL_ITEM_DESIGNATORIDX_PREFIX 0x38 /* Designator Index */
#define HID_LOCAL_ITEM_DESIGNATORMIN_PREFIX 0x48 /* Designator Minimum */
#define HID_LOCAL_ITEM_DESIGNATORMAX_PREFIX 0x58 /* Designator Maximum */
#define HID_LOCAL_ITEM_STRINGIDX_PREFIX 0x78 /* String Index */
#define HID_LOCAL_ITEM_STRINGMIN_PREFIX 0x88 /* String Minimum */
#define HID_LOCAL_ITEM_STRINGMAX_PREFIX 0x98 /* xx */
#define HID_LOCAL_ITEM_DELIMITER_PREFIX 0xa8 /* Delimiter */
#define HID_LOCALITEM_TAG_USAGE (0x00 << 4)
#define HID_LOCALITEM_TAG_USAGE_MIN (0x01 << 4)
#define HID_LOCALITEM_TAG_USAGE_MAX (0x02 << 4)
#define HID_LOCALITEM_TAG_DESIG_INDEX (0x03 << 4)
#define HID_LOCALITEM_TAG_DESIG_MIN (0x04 << 4)
#define HID_LOCALITEM_TAG_DESIG_MAX (0x05 << 4)
/* No 6 in spec */
#define HID_LOCALITEM_TAG_STRING_INDEX (0x07 << 4)
#define HID_LOCALITEM_TAG_STRING_MIN (0x08 << 4)
#define HID_LOCALITEM_TAG_STRING_MAX (0x09 << 4)
#define HID_LOCALITEM_TAG_DELIMITER (0x0a << 4) /* Also listed as reserved in spec! */
/* Usage pages (HuT 3) */
#define HID_USAGE_PAGE_UNDEFINED 0x00 /* Undefined */
#define HID_USAGE_PAGE_GENERIC_DESKTOP_CONTROLS 0x01 /* Generic Desktop Controls */
#define HID_USAGE_PAGE_SIMULATION_CONTROLS 0x02 /* Simulation Controls */
#define HID_USAGE_PAGE_VR_CONTROLS 0x03 /* VR Controls */
#define HID_USAGE_PAGE_SPORT_CONTROLS 0x04 /* Sport Controls */
#define HID_USAGE_PAGE_GAME_CONTROLS 0x05 /* Game Controls */
#define HID_USAGE_PAGE_GENERIC_DEVICE_CONTROLS 0x06 /* Generic Device Controls */
#define HID_USAGE_PAGE_KEYBOARD_KEYPAD 0x07 /* Keyboard/Keypad */
#define HID_USAGE_PAGE_LED 0x08 /* LEDs */
#define HID_USAGE_PAGE_BUTTON 0x09 /* Button */
#define HID_USAGE_PAGE_ORDINAL 0x0a /* Ordinal */
#define HID_USAGE_PAGE_TELEPHONY 0x0b /* Telephony */
#define HID_USAGE_PAGE_CONSUMER 0x0c /* Consumer */
#define HID_USAGE_PAGE_DIGITIZER 0x0d /* Digitizer */
#define HID_USAGE_PAGE_HAPTICS /* 0x0e Reserved */
#define HID_USAGE_PAGE_PID 0x0f /* PID Page Physical Interface Device */
#define HID_USAGE_PAGE_UNICODE 0x10 /* Unicode */
#define HID_USAGE_PAGE_SOC 0x11 /* Sensor Orientation Category */
#define HID_USAGE_PAGE_EYE_AND_HEAD_TRACKER 0x12 /* Eye and Head Tracker */
/* 0x13 Reserved */
#define HID_USAGE_PAGE_ALPHA_DISPLAY 0x14 /* Alphanumeric Display */
/* 0x15-3f Reserved */
#define HID_USAGE_PAGE_MEDICAL 0x40 /* Medical Instruments */
#define HID_USAGE_PAGE_BRAILLE_DISPLAY 0x41 /* Braille Display */
/* 0x42-0x58 Reserved */
#define HID_USAGE_PAGE_LIGHTING_AND_ILLUMINATION 0x59 /* Lighting and Illumination */
/* 0x5a-0x7f Reserved */
#define HID_USAGE_PAGE_USB_MONITOR 0x80 /* USB Monitor */
#define HID_USAGE_PAGE_USB_ENUMERATED_VALUES 0x81 /* USB Enumerated Values */
#define HID_USAGE_PAGE_VESA_VIRTUAL_CONTROLS 0x82 /* VESA Virtual Controls */
#define HID_USAGE_PAGE_POWER_DEVICE 0x84 /* Power Device */
#define HID_USAGE_PAGE_BATTERY_SYSTEM 0x85 /* Battery System */
#define HID_USAGE_PAGE_BARCODE_SCANNER 0x8c /* Bar Code Scanner page */
#define HID_USAGE_PAGE_SCALE 0x8d /* Scale page */
#define HID_USAGE_PAGE_MSR 0x8e /* Magnetic Stripe Reading (MSR) Devices */
#define HID_USAGE_PAGE_POS 0x8f /* Point of Sale devices */
#define HID_USAGE_PAGE_CAMERA_CONTROL 0x90 /* Camera Control Page */
#define HID_USAGE_PAGE_ARCADE 0x91
#define HID_USAGE_PAGE_GAMING_DEVICE 0x92
#define HID_USAGE_PAGE_FIDO_ALLIANCE 0xF1D0
#define HID_USAGE_PAGE_VENDOR_PAGE_HBYTE 0xFF00
/* Modifier Keys (HID 8.3) */
#define HID_MODIFIER_LCTRL (1 << 0) /* Left Ctrl */
@@ -205,38 +220,6 @@
#define HID_JS_INPUT_REPORT_BUTTON3 (1 << 6)
#define HID_JS_INPUT_REPORT_BUTTON4 (1 << 7)
/* Usage pages (HuT 3) */
#define HID_USAGE_PAGE_UNDEFINED 0x00 /* Undefined */
#define HID_USAGE_PAGE_GENERIC_DCTRL 0x01 /* Generic Desktop Controls */
#define HID_USAGE_PAGE_SIMCTRL 0x02 /* Simulation Controls */
#define HID_USAGE_PAGE_VRCTRL 0x03 /* VR Controls */
#define HID_USAGE_PAGE_SPORTCTRL 0x04 /* Sport Controls */
#define HID_USAGE_PAGE_GAMECTRL 0x05 /* Game Controls */
#define HID_USAGE_PAGE_GENERIC_DEVCTRL 0x06 /* Generic Device Controls */
#define HID_USAGE_PAGE_KBD 0x07 /* Keyboard/Keypad */
#define HID_USAGE_PAGE_LEDS 0x08 /* LEDs */
#define HID_USAGE_PAGE_BUTTON 0x09 /* Button */
#define HID_USAGE_PAGE_ORDINAL 0x0a /* Ordinal */
#define HID_USAGE_PAGE_TELEPHONY 0x0b /* Telephony */
#define HID_USAGE_PAGE_CONSUMER 0x0c /* Consumer */
#define HID_USAGE_PAGE_DIGITIZER 0x0d /* Digitizer */
/* 0x0e Reserved */
#define HID_USAGE_PAGE_PIDPAGE 0x0f /* PID Page Physical Interface Device */
#define HID_USAGE_PAGE_UNICODE 0x10 /* Unicode */
/* 0x11-13 Reserved */
#define HID_USAGE_PAGE_ALPHA_DISPLAY 0x14 /* Alphanumeric Display */
/* 0x15-3f Reserved */
#define HID_USAGE_PAGE_MEDICAL 0x40 /* Medical Instruments */
/* 0x41-7f Reserved */
/* 0x80-83 Monitor Devices */
/* 0x84-87 Power Devices */
/* 0x88-8b Reserved */
#define HID_USAGE_PAGE_BARCODE_SCANNER 0x8c /* Bar Code Scanner page */
#define HID_USAGE_PAGE_SCALE 0x8d /* Scale page */
#define HID_USAGE_PAGE_MSR 0x8e /* Magnetic Stripe Reading (MSR) Devices */
#define HID_USAGE_PAGE_POS 0x8f /* Point of Sale devices */
#define HID_USAGE_PAGE_CAMERA_CTRL 0x90 /* Camera Control Page */
/* Generic Desktop Page Usage IDs (HuT 4) */
#define HID_DESKTOP_USAGE_UNDEFINED 0x00 /* Undefined */
#define HID_DESKTOP_USAGE_POINTER 0x01 /* Pointer */
@@ -634,7 +617,7 @@ struct usb_hid_js_report {
#define HID_CUSTOM_INOUT_DESCRIPTOR_LEN (9 + 9 + 7 + 7)
#define HID_CUSTOM_INOUT_DESCRIPTOR_INIT(bInterfaceNumber, bInterfaceSubClass, wItemLength, in_ep, out_ep,wMaxPacketSize, bInterval) \
#define HID_CUSTOM_INOUT_DESCRIPTOR_INIT(bInterfaceNumber, bInterfaceSubClass, wItemLength, out_ep, in_ep, wMaxPacketSize, bInterval) \
0x09, /* bLength: Interface Descriptor size */ \
USB_DESCRIPTOR_TYPE_INTERFACE, /* bDescriptorType: Interface descriptor type */ \
bInterfaceNumber, /* bInterfaceNumber: Number of Interface */ \
@@ -654,13 +637,13 @@ struct usb_hid_js_report {
WBVAL(wItemLength), /* wItemLength: Total length of Report descriptor */ \
0x07, /* bLength: Endpoint Descriptor size */ \
USB_DESCRIPTOR_TYPE_ENDPOINT, /* bDescriptorType: */ \
in_ep, /* bEndpointAddress: Endpoint Address (IN) */ \
out_ep, /* bEndpointAddress: Endpoint Address (OUT) */ \
0x03, /* bmAttributes: Interrupt endpoint */ \
WBVAL(wMaxPacketSize), /* wMaxPacketSize: x Byte max */ \
bInterval, /* bInterval: Polling Interval */ \
0x07, /* bLength: Endpoint Descriptor size */ \
USB_DESCRIPTOR_TYPE_ENDPOINT, /* bDescriptorType: */ \
out_ep, /* bEndpointAddress: Endpoint Address (IN) */ \
in_ep, /* bEndpointAddress: Endpoint Address (IN) */ \
0x03, /* bmAttributes: Interrupt endpoint */ \
WBVAL(wMaxPacketSize), /* wMaxPacketSize: x Byte max */ \
bInterval /* bInterval: Polling Interval */

File diff suppressed because it is too large Load Diff

View File

@@ -8,6 +8,82 @@
#include "usb_hid.h"
/* local items */
#define HID_REPORT_FLAG_USAGE_MIN (1 << 0)
#define HID_REPORT_FLAG_USAGE_MAX (1 << 1)
/* global items */
#define HID_REPORT_FLAG_REPORT_ID (1 << 2)
#define HID_REPORT_FLAG_REPORT_COUNT (1 << 3)
#define HID_REPORT_FLAG_REPORT_SIZE (1 << 4)
#define HID_REPORT_FLAG_LOGICAL_MIN (1 << 5)
#define HID_REPORT_FLAG_LOGICAL_MAX (1 << 6)
#define HID_REPORT_FLAG_USAGE_PAGE (1 << 7)
/* main items */
#define HID_REPORT_FLAG_INPUT (1 << 8)
#define HID_REPORT_FLAG_OUTPUT (1 << 9)
#define HID_REPORT_FLAG_FEATURE (1 << 10)
#define HID_REPORT_FLAG_EXTENDED_USAGE (1 << 11)
/* masks */
#define HID_REPORT_FLAG_GLOBAL_MASK (HID_REPORT_FLAG_REPORT_ID | \
HID_REPORT_FLAG_REPORT_COUNT | \
HID_REPORT_FLAG_REPORT_SIZE | \
HID_REPORT_FLAG_LOGICAL_MIN | \
HID_REPORT_FLAG_LOGICAL_MAX | \
HID_REPORT_FLAG_USAGE_PAGE)
#define HID_REPORT_FLAG_REQUIRED_MASK (HID_REPORT_FLAG_REPORT_COUNT | \
HID_REPORT_FLAG_REPORT_SIZE | \
HID_REPORT_FLAG_LOGICAL_MIN | \
HID_REPORT_FLAG_LOGICAL_MAX)
#define USAGE_ID(usage) (usage & 0x0000FFFF)
#define USAGE_PAGE(usage) ((usage & 0xFFFF0000) >> 16)
#ifndef CONFIG_USBHOST_HID_MAX_INPUT
#define CONFIG_USBHOST_HID_MAX_INPUT 16
#endif
#ifndef CONFIG_USBHOST_HID_MAX_OUTPUT
#define CONFIG_USBHOST_HID_MAX_OUTPUT 16
#endif
#ifndef CONFIG_USBHOST_HID_MAX_FEATURE
#define CONFIG_USBHOST_HID_MAX_FEATURE 16
#endif
struct hid_report_field {
uint32_t *usages; /* usage page + usage */
uint32_t usage_count;
uint32_t usage_page;
uint32_t report_id; /* optional */
uint32_t report_count;
uint32_t report_size;
int32_t logical_min;
int32_t logical_max;
uint32_t properties;
uint32_t usage_min;
uint32_t usage_max;
uint32_t flags;
};
struct hid_report {
bool uses_report_id;
uint32_t input_count;
struct hid_report_field input_fields[CONFIG_USBHOST_HID_MAX_INPUT];
uint32_t output_count;
struct hid_report_field output_fields[CONFIG_USBHOST_HID_MAX_OUTPUT];
uint32_t feature_count;
struct hid_report_field feature_fields[CONFIG_USBHOST_HID_MAX_FEATURE];
};
struct usbh_hid {
struct usbh_hubport *hport;
struct usb_endpoint_descriptor *intin; /* INTR IN endpoint */
@@ -36,9 +112,14 @@ int usbh_hid_get_protocol(struct usbh_hid *hid_class, uint8_t *protocol);
int usbh_hid_set_report(struct usbh_hid *hid_class, uint8_t report_type, uint8_t report_id, uint8_t *buffer, uint32_t buflen);
int usbh_hid_get_report(struct usbh_hid *hid_class, uint8_t report_type, uint8_t report_id, uint8_t *buffer, uint32_t buflen);
struct hid_report *usbh_hid_report_parse(const uint8_t *data, uint32_t report_len, uint32_t max_usages);
void usbh_hid_report_free(struct hid_report *hid_report);
void usbh_hid_run(struct usbh_hid *hid_class);
void usbh_hid_stop(struct usbh_hid *hid_class);
int lshid(int argc, char **argv);
#ifdef __cplusplus
}
#endif

View File

@@ -238,6 +238,7 @@ int usbh_hub_set_feature(struct usbh_hub *hub, uint8_t port, uint8_t feature)
{
struct usb_setup_packet roothub_setup;
struct usb_setup_packet *setup;
int ret;
if (hub->is_roothub) {
setup = &roothub_setup;
@@ -246,9 +247,22 @@ int usbh_hub_set_feature(struct usbh_hub *hub, uint8_t port, uint8_t feature)
setup->wValue = feature;
setup->wIndex = port;
setup->wLength = 0;
return usbh_roothub_control(hub->bus, setup, NULL);
ret = usbh_roothub_control(hub->bus, setup, NULL);
if ((feature == HUB_PORT_FEATURE_RESET) && (ret >= 0)) {
hub->bus->event_handler(hub->bus->busid, hub->index, port, USB_INTERFACE_ANY, USBH_EVENT_DEVICE_RESET);
}
return ret;
} else {
return _usbh_hub_set_feature(hub, port, feature);
ret = _usbh_hub_set_feature(hub, port, feature);
if ((feature == HUB_PORT_FEATURE_RESET) && (ret >= 0)) {
hub->bus->event_handler(hub->bus->busid, hub->index, port, USB_INTERFACE_ANY, USBH_EVENT_DEVICE_RESET);
}
return ret;
}
}
@@ -337,11 +351,11 @@ static int usbh_hub_connect(struct usbh_hubport *hport, uint8_t intf)
}
/*
* Super-Speed hubs need to know their depth to be able to
* parse the bits of the route-string that correspond to
* their downstream port number.
*
*/
* Super-Speed hubs need to know their depth to be able to
* parse the bits of the route-string that correspond to
* their downstream port number.
*
*/
if ((hport->depth != 0) && (hport->speed == USB_SPEED_SUPER)) {
ret = usbh_hub_set_depth(hub, hport->depth - 1);
if (ret < 0) {
@@ -373,6 +387,11 @@ static int usbh_hub_connect(struct usbh_hubport *hport, uint8_t intf)
hub->tt_think = ((hub->hub_desc.wHubCharacteristics & HUB_CHAR_TTTT_MASK) >> 5);
}
if (hub->nports > CONFIG_USBHOST_MAX_EHPORTS) {
USB_LOG_ERR("Hub nports %u overflow\r\n", hub->nports);
return -USB_ERR_NOMEM;
}
for (uint8_t port = 0; port < hub->nports; port++) {
hub->child[port].port = port + 1;
hub->child[port].parent = hub;
@@ -470,6 +489,8 @@ static void usbh_hub_events(struct usbh_hub *hub)
int ret;
size_t flags;
(void)speed_table;
if (!hub->connected) {
return;
}
@@ -560,6 +581,8 @@ static void usbh_hub_events(struct usbh_hub *hub)
/* Last, check connect status */
if (portstatus & HUB_PORT_STATUS_CONNECTION) {
hub->bus->event_handler(hub->bus->busid, hub->index, port + 1, USB_INTERFACE_ANY, USBH_EVENT_DEVICE_CONNECTED);
ret = usbh_hub_set_feature(hub, port + 1, HUB_PORT_FEATURE_RESET);
if (ret < 0) {
USB_LOG_ERR("Failed to reset port %u, errorcode: %d\r\n", port + 1, ret);
@@ -641,7 +664,6 @@ static void usbh_hub_events(struct usbh_hub *hub)
child = &hub->child[port];
/** release child sources */
usbh_hubport_release(child);
USB_LOG_INFO("Device on Bus %u, Hub %u, Port %u disconnected\r\n", hub->bus->busid, hub->index, port + 1);
}
}
}
@@ -660,12 +682,15 @@ static void usbh_hub_thread(CONFIG_USB_OSAL_THREAD_SET_ARGV)
struct usbh_bus *bus = (struct usbh_bus *)CONFIG_USB_OSAL_THREAD_GET_ARGV;
usb_hc_init(bus);
bus->event_handler(bus->busid, USB_HUB_INDEX_ANY, USB_HUB_PORT_ANY, USB_INTERFACE_ANY, USBH_EVENT_INIT);
while (1) {
ret = usb_osal_mq_recv(bus->hub_mq, (uintptr_t *)&hub, USB_OSAL_WAITING_FOREVER);
if (ret < 0) {
continue;
}
usb_osal_mutex_take(bus->mutex);
usbh_hub_events(hub);
usb_osal_mutex_give(bus->mutex);
}
}
@@ -695,6 +720,12 @@ int usbh_hub_initialize(struct usbh_bus *bus)
return -1;
}
bus->mutex = usb_osal_mutex_create();
if (bus->mutex == NULL) {
USB_LOG_ERR("Failed to create bus mutex\r\n");
return -1;
}
snprintf(thread_name, 32, "usbh_hub%u", bus->busid);
bus->hub_thread = usb_osal_thread_create(thread_name, CONFIG_USBHOST_PSC_STACKSIZE, CONFIG_USBHOST_PSC_PRIO, usbh_hub_thread, bus);
if (bus->hub_thread == NULL) {
@@ -708,8 +739,8 @@ int usbh_hub_deinitialize(struct usbh_bus *bus)
{
struct usbh_hubport *hport;
struct usbh_hub *hub;
size_t flags;
usb_osal_mutex_take(bus->mutex);
hub = &bus->hcd.roothub;
for (uint8_t port = 0; port < hub->nports; port++) {
hport = &hub->child[port];
@@ -717,15 +748,13 @@ int usbh_hub_deinitialize(struct usbh_bus *bus)
usbh_hubport_release(hport);
}
flags = usb_osal_enter_critical_section();
usb_hc_deinit(bus);
usb_osal_leave_critical_section(flags);
usb_osal_mq_delete(bus->hub_mq);
usb_osal_thread_delete(bus->hub_thread);
usb_osal_mq_delete(bus->hub_mq);
usb_osal_mutex_give(bus->mutex);
usb_osal_mutex_delete(bus->mutex);
return 0;
}

View File

@@ -6,6 +6,8 @@
#ifndef USB_MIDI_H
#define USB_MIDI_H
#include "usb_audio.h"
/* bDescriptorSubType */
#define MIDI_VC_HEADER_DESCRIPTOR_SUBTYPE 0x01U
#define MIDI_MS_HEADER_DESCRIPTOR_SUBTYPE 0x01U
@@ -201,6 +203,19 @@ struct midi_cs_ep_ms_general_descriptor {
#define MIDI_SIZEOF_MS_GENERAL_DESC(n) (4 + n)
// clang-format off
#define MIDI_STANDARD_DESCRIPTOR_INIT(bInterfaceNumber, bNumEndpoints) \
0x09, /* bLength */ \
USB_DESCRIPTOR_TYPE_INTERFACE, /* bDescriptorType */ \
bInterfaceNumber, /* bInterfaceNumber */ \
0x00, /* bAlternateSetting */ \
bNumEndpoints, /* bNumEndpoints */ \
USB_DEVICE_CLASS_AUDIO, /* bInterfaceClass */ \
AUDIO_SUBCLASS_MIDISTREAMING, /* bInterfaceSubClass */ \
AUDIO_PROTOCOL_UNDEFINED, /* bInterfaceProtocol */ \
0x00 /* iInterface */
#define MIDI_STANDARD_DESCRIPTOR_LEN 0x09
#define MIDI_CS_HEADER_DESCRIPTOR_INIT(wTotalLength) \
0x07, /* bLength */ \
USB_CS_DESCRIPTOR_TYPE_INTERFACE, /* bDescriptorType */ \

View File

@@ -0,0 +1,266 @@
/*
* Copyright (c) 2022 ~ 2025, sakumisu
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "usbh_core.h"
#include "usbh_serial.h"
#include "usbh_cdc_acm.h"
#undef USB_DBG_TAG
#define USB_DBG_TAG "usbh_cdc_acm"
#include "usb_log.h"
struct usbh_cdc_acm {
struct usb_endpoint_descriptor *intin;
struct usbh_urb intin_urb;
struct usb_osal_timer *modem_timer;
uint16_t modem_status;
};
static int usbh_cdc_acm_attach(struct usbh_serial *serial)
{
struct usb_endpoint_descriptor *ep_desc;
int ret;
struct usbh_cdc_acm *cdc_acm_class = usb_osal_malloc(sizeof(struct usbh_cdc_acm));
if (!cdc_acm_class) {
USB_LOG_ERR("No memory for cdc_acm_class\r\n");
return -USB_ERR_NOMEM;
}
memset(cdc_acm_class, 0, sizeof(struct usbh_cdc_acm));
serial->priv = cdc_acm_class;
for (uint8_t i = 0; i < serial->hport->config.intf[serial->intf].altsetting[0].intf_desc.bNumEndpoints; i++) {
ep_desc = &serial->hport->config.intf[serial->intf].altsetting[0].ep[i].ep_desc;
if (USB_GET_ENDPOINT_TYPE(ep_desc->bmAttributes) == USB_ENDPOINT_TYPE_INTERRUPT) {
if (ep_desc->bEndpointAddress & 0x80) {
USBH_EP_INIT(cdc_acm_class->intin, ep_desc);
break;
} else {
}
}
}
if (!cdc_acm_class->intin) {
USB_LOG_ERR("Failed to find interrupt endpoint\r\n");
ret = -USB_ERR_NODEV;
goto errout;
}
return 0;
errout:
serial->priv = NULL;
usb_osal_free(cdc_acm_class);
return ret;
}
static void usbh_cdc_acm_detach(struct usbh_serial *serial)
{
struct usbh_cdc_acm *cdc_acm_class;
if (!serial || !serial->priv) {
return;
}
cdc_acm_class = (struct usbh_cdc_acm *)serial->priv;
if (cdc_acm_class->intin) {
usbh_kill_urb(&cdc_acm_class->intin_urb);
}
serial->priv = NULL;
usb_osal_free(cdc_acm_class);
}
static int usbh_cdc_acm_set_line_coding(struct usbh_serial *serial, struct cdc_line_coding *line_coding)
{
struct usb_setup_packet *setup;
if (!serial || !serial->hport) {
return -USB_ERR_INVAL;
}
setup = serial->hport->setup;
setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE;
setup->bRequest = CDC_REQUEST_SET_LINE_CODING;
setup->wValue = 0;
setup->wIndex = serial->intf;
setup->wLength = 7;
memcpy(serial->iobuffer, line_coding, sizeof(struct cdc_line_coding));
return usbh_control_transfer(serial->hport, setup, serial->iobuffer);
}
static int usbh_cdc_acm_get_line_coding(struct usbh_serial *serial, struct cdc_line_coding *line_coding)
{
struct usb_setup_packet *setup;
int ret;
if (!serial || !serial->hport) {
return -USB_ERR_INVAL;
}
setup = serial->hport->setup;
setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE;
setup->bRequest = CDC_REQUEST_GET_LINE_CODING;
setup->wValue = 0;
setup->wIndex = serial->intf;
setup->wLength = 7;
ret = usbh_control_transfer(serial->hport, setup, serial->iobuffer);
if (ret < 0) {
return ret;
}
memcpy(line_coding, serial->iobuffer, sizeof(struct cdc_line_coding));
return ret;
}
static int usbh_cdc_acm_set_line_state(struct usbh_serial *serial, bool dtr, bool rts)
{
struct usb_setup_packet *setup;
if (!serial || !serial->hport) {
return -USB_ERR_INVAL;
}
setup = serial->hport->setup;
setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE;
setup->bRequest = CDC_REQUEST_SET_CONTROL_LINE_STATE;
setup->wValue = (dtr << 0) | (rts << 1);
setup->wIndex = serial->intf;
setup->wLength = 0;
return usbh_control_transfer(serial->hport, setup, NULL);
}
static int usbh_cdc_acm_get_modem_status(struct usbh_serial *serial)
{
struct usbh_cdc_acm *cdc_acm_class;
uintptr_t flags;
uint16_t status;
if (!serial || !serial->hport || !serial->priv) {
return -USB_ERR_INVAL;
}
flags = usb_osal_enter_critical_section();
cdc_acm_class = (struct usbh_cdc_acm *)serial->priv;
status = (cdc_acm_class->modem_status & CDC_SERIAL_STATE_DSR ? 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_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_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_DSR)
serial->iocount.dsr++;
if (difference & CDC_SERIAL_STATE_DCD)
serial->iocount.dcd++;
if (notification->data & CDC_SERIAL_STATE_BREAK)
serial->iocount.brk++;
if (notification->data & CDC_SERIAL_STATE_FRAMING)
serial->iocount.frame++;
if (notification->data & CDC_SERIAL_STATE_PARITY)
serial->iocount.parity++;
if (notification->data & CDC_SERIAL_STATE_OVERRUN)
serial->iocount.overrun++;
usb_osal_leave_critical_section(flags);
return ret;
}
#endif
static const struct usbh_serial_driver cdc_acm_driver = {
.driver_name = "cdc_acm",
.ignore_rx_header = 0,
.ignore_tx_header = 0,
.attach = usbh_cdc_acm_attach,
.detach = usbh_cdc_acm_detach,
.set_flow_control = NULL,
.set_line_coding = usbh_cdc_acm_set_line_coding,
.get_line_coding = usbh_cdc_acm_get_line_coding,
.set_line_state = usbh_cdc_acm_set_line_state,
.get_modem_status = usbh_cdc_acm_get_modem_status,
};
static int usbh_cdc_acm_connect(struct usbh_hubport *hport, uint8_t intf)
{
return usbh_serial_probe(hport, intf, &cdc_acm_driver) ? 0 : -USB_ERR_NOMEM;
}
static int usbh_cdc_acm_disconnect(struct usbh_hubport *hport, uint8_t intf)
{
struct usbh_serial *serial = (struct usbh_serial *)hport->config.intf[intf].priv;
if (serial) {
usbh_serial_remove(serial);
}
return 0;
}
const struct usbh_class_driver cdc_acm_class_driver = {
.driver_name = "cdc_acm",
.connect = usbh_cdc_acm_connect,
.disconnect = usbh_cdc_acm_disconnect
};
CLASS_INFO_DEFINE const struct usbh_class_info cdc_acm_none_class_info = {
.match_flags = USB_CLASS_MATCH_INTF_CLASS | USB_CLASS_MATCH_INTF_SUBCLASS | USB_CLASS_MATCH_INTF_PROTOCOL,
.bInterfaceClass = USB_DEVICE_CLASS_CDC,
.bInterfaceSubClass = CDC_ABSTRACT_CONTROL_MODEL,
.bInterfaceProtocol = CDC_COMMON_PROTOCOL_NONE,
.id_table = NULL,
.class_driver = &cdc_acm_class_driver
};
CLASS_INFO_DEFINE const struct usbh_class_info cdc_acm_at_class_info = {
.match_flags = USB_CLASS_MATCH_INTF_CLASS | USB_CLASS_MATCH_INTF_SUBCLASS | USB_CLASS_MATCH_INTF_PROTOCOL,
.bInterfaceClass = USB_DEVICE_CLASS_CDC,
.bInterfaceSubClass = CDC_ABSTRACT_CONTROL_MODEL,
.bInterfaceProtocol = CDC_COMMON_PROTOCOL_AT_COMMANDS,
.id_table = NULL,
.class_driver = &cdc_acm_class_driver
};

View File

@@ -0,0 +1,19 @@
/*
* Copyright (c) 2022 ~ 2025, sakumisu
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef USBH_CDC_ACM_H
#define USBH_CDC_ACM_H
#include "usb_cdc.h"
#ifdef __cplusplus
extern "C" {
#endif
#ifdef __cplusplus
}
#endif
#endif /* USBH_CDC_ACM_H */

View File

@@ -0,0 +1,409 @@
/*
* 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;
}
usbh_ch34x_get_baudrate_div(line_coding->dwDTERate, &factor, &divisor);
reg_value |= 0xC0;
value |= 0x9c;
value |= reg_value << 8;
index |= 0x80 | divisor;
index |= (uint16_t)factor << 8;
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
};

View File

@@ -0,0 +1,56 @@
/*
* Copyright (c) 2024 ~ 2025, sakumisu
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef USBH_CH34X_H
#define USBH_CH34X_H
#include "usb_cdc.h"
/* Requests */
#define CH34X_READ_VERSION 0x5F
#define CH34X_WRITE_REG 0x9A
#define CH34X_READ_REG 0x95
#define CH34X_SERIAL_INIT 0xA1
#define CH34X_MODEM_CTRL 0xA4
// modem control bits
#define CH34X_BIT_RTS (1 << 6)
#define CH34X_BIT_DTR (1 << 5)
#define CH341_CTO_O 0x10
#define CH341_CTO_D 0x20
#define CH341_CTO_R 0x40
#define CH341_CTI_C 0x01
#define CH341_CTI_DS 0x02
#define CH341_CTRL_RI 0x04
#define CH341_CTI_DC 0x08
#define CH341_CTI_ST 0x0f
#define CH341_CTT_M BIT(3)
#define CH341_CTT_F (BIT(2) | BIT(6))
#define CH341_CTT_P BIT(2)
#define CH341_CTT_O BIT(1)
#define CH341_L_ER 0x80
#define CH341_L_ET 0x40
#define CH341_L_PS 0x38
#define CH341_L_PM 0x28
#define CH341_L_PE 0x18
#define CH341_L_PO 0x08
#define CH341_L_SB 0x04
#define CH341_L_D8 0x03
#define CH341_L_D7 0x02
#define CH341_L_D6 0x01
#define CH341_L_D5 0x00
#ifdef __cplusplus
extern "C" {
#endif
#ifdef __cplusplus
}
#endif
#endif /* USBH_CH34X_H */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,187 @@
/*
* Copyright (c) 2024 ~ 2025, sakumisu
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef USBH_CP210X_H
#define USBH_CP210X_H
#include "usb_cdc.h"
/* Requests */
#define CP210X_IFC_ENABLE 0x00
#define CP210X_SET_BAUDDIV 0x01
#define CP210X_GET_BAUDDIV 0x02
#define CP210X_SET_LINE_CTL 0x03 // Set parity, data bits, stop bits
#define CP210X_GET_LINE_CTL 0x04
#define CP210X_SET_BREAK 0x05
#define CP210X_IMM_CHAR 0x06
#define CP210X_SET_MHS 0x07 // Set DTR, RTS
#define CP210X_GET_MDMSTS 0x08
#define CP210X_SET_XON 0x09
#define CP210X_SET_XOFF 0x0A
#define CP210X_SET_EVENTMASK 0x0B
#define CP210X_GET_EVENTMASK 0x0C
#define CP210X_SET_CHAR 0x0D
#define CP210X_GET_CHARS 0x0E
#define CP210X_GET_PROPS 0x0F
#define CP210X_GET_COMM_STATUS 0x10
#define CP210X_RESET 0x11
#define CP210X_PURGE 0x12
#define CP210X_SET_FLOW 0x13
#define CP210X_GET_FLOW 0x14
#define CP210X_EMBED_EVENTS 0x15
#define CP210X_GET_EVENTSTATE 0x16
#define CP210X_SET_CHARS 0x19
#define CP210X_GET_BAUDRATE 0x1D
#define CP210X_SET_BAUDRATE 0x1E // Set baudrate
#define CP210X_VENDOR_SPECIFIC 0xFF
/* CP210X_VENDOR_SPECIFIC values */
#define CP210X_GET_FW_VER 0x000E
#define CP210X_READ_2NCONFIG 0x000E
#define CP210X_GET_FW_VER_2N 0x0010
#define CP210X_READ_LATCH 0x00C2
#define CP210X_GET_PARTNUM 0x370B
#define CP210X_GET_PORTCONFIG 0x370C
#define CP210X_GET_DEVICEMODE 0x3711
#define CP210X_WRITE_LATCH 0x37E1
/* CP210X_IFC_ENABLE */
#define CP210X_UART_ENABLE 0x0001
#define CP210X_UART_DISABLE 0x0000
/* CP210X_(SET|GET)_BAUDDIV */
#define CP210X_BAUD_RATE_GEN_FREQ 0x384000
/* CP210X_(SET|GET)_LINE_CTL */
#define CP210X_BITS_DATA_MASK 0X0f00
#define CP210X_BITS_DATA_5 0X0500
#define CP210X_BITS_DATA_6 0X0600
#define CP210X_BITS_DATA_7 0X0700
#define CP210X_BITS_DATA_8 0X0800
#define CP210X_BITS_DATA_9 0X0900
#define CP210X_BITS_PARITY_MASK 0x00f0
#define CP210X_BITS_PARITY_NONE 0x0000
#define CP210X_BITS_PARITY_ODD 0x0010
#define CP210X_BITS_PARITY_EVEN 0x0020
#define CP210X_BITS_PARITY_MARK 0x0030
#define CP210X_BITS_PARITY_SPACE 0x0040
#define CP210X_BITS_STOP_MASK 0x000f
#define CP210X_BITS_STOP_1 0x0000
#define CP210X_BITS_STOP_1_5 0x0001
#define CP210X_BITS_STOP_2 0x0002
/* CP210X_SET_BREAK */
#define CP210X_BREAK_ON 0x0001
#define CP210X_BREAK_OFF 0x0000
/* CP210X_(SET_MHS|GET_MDMSTS) */
#define CP210X_CONTROL_DTR 0x0001
#define CP210X_CONTROL_RTS 0x0002
#define CP210X_CONTROL_CTS 0x0010
#define CP210X_CONTROL_DSR 0x0020
#define CP210X_CONTROL_RING 0x0040
#define CP210X_CONTROL_DCD 0x0080
#define CP210X_CONTROL_WRITE_DTR 0x0100
#define CP210X_CONTROL_WRITE_RTS 0x0200
/* CP210X_(GET|SET)_CHARS */
struct cp210x_special_chars {
uint8_t bEofChar;
uint8_t bErrorChar;
uint8_t bBreakChar;
uint8_t bEventChar;
uint8_t bXonChar;
uint8_t bXoffChar;
};
/* CP210X_GET_COMM_STATUS returns these 0x13 bytes */
struct cp210x_comm_status {
uint32_t ulErrors;
uint32_t ulHoldReasons;
uint32_t ulAmountInInQueue;
uint32_t ulAmountInOutQueue;
uint8_t bEofReceived;
uint8_t bWaitForImmediate;
uint8_t bReserved;
} __PACKED;
/*
* CP210X_PURGE - 16 bits passed in wValue of USB request.
* SiLabs app note AN571 gives a strange description of the 4 bits:
* bit 0 or bit 2 clears the transmit queue and 1 or 3 receive.
* writing 1 to all, however, purges cp2108 well enough to avoid the hang.
*/
#define PURGE_ALL 0x000f
/* CP210X_EMBED_EVENTS */
#define CP210X_ESCCHAR 0xec
#define CP210X_LSR_OVERRUN BIT(1)
#define CP210X_LSR_PARITY BIT(2)
#define CP210X_LSR_FRAME BIT(3)
#define CP210X_LSR_BREAK BIT(4)
/* CP210X_GET_FLOW/CP210X_SET_FLOW read/write these 0x10 bytes */
struct cp210x_flow_ctl {
uint32_t lControlHandshake;
uint32_t lFlowReplace;
uint32_t lXonLimit;
uint32_t lXoffLimit;
};
/* cp210x_flow_ctl::ulControlHandshake */
#define CP210X_SERIAL_DTR_MASK (0x03 << 0)
#define CP210X_SERIAL_DTR_INACTIVE (0 << 0)
#define CP210X_SERIAL_DTR_ACTIVE (1 << 0)
#define CP210X_SERIAL_DTR_FLOW_CTL (2 << 0)
#define CP210X_SERIAL_CTS_HANDSHAKE BIT(3)
#define CP210X_SERIAL_DSR_HANDSHAKE BIT(4)
#define CP210X_SERIAL_DCD_HANDSHAKE BIT(5)
#define CP210X_SERIAL_DSR_SENSITIVITY BIT(6)
/* cp210x_flow_ctl::ulFlowReplace */
#define CP210X_SERIAL_AUTO_TRANSMIT BIT(0)
#define CP210X_SERIAL_AUTO_RECEIVE BIT(1)
#define CP210X_SERIAL_ERROR_CHAR BIT(2)
#define CP210X_SERIAL_NULL_STRIPPING BIT(3)
#define CP210X_SERIAL_BREAK_CHAR BIT(4)
#define CP210X_SERIAL_RTS_MASK (0x03 << 6)
#define CP210X_SERIAL_RTS_INACTIVE (0 << 6)
#define CP210X_SERIAL_RTS_ACTIVE (1 << 6)
#define CP210X_SERIAL_RTS_FLOW_CTL (2 << 6)
#define CP210X_SERIAL_XOFF_CONTINUE BIT(31)
/* CP210X_VENDOR_SPECIFIC, CP210X_GET_DEVICEMODE call reads these 0x2 bytes. */
struct cp210x_pin_mode {
uint8_t eci;
uint8_t sci;
};
#define CP210X_PIN_MODE_MODEM 0
#define CP210X_PIN_MODE_GPIO BIT(0)
/* Part number definitions */
#define CP210X_PARTNUM_CP2101 0x01
#define CP210X_PARTNUM_CP2102 0x02
#define CP210X_PARTNUM_CP2103 0x03
#define CP210X_PARTNUM_CP2104 0x04
#define CP210X_PARTNUM_CP2105 0x05
#define CP210X_PARTNUM_CP2108 0x08
#define CP210X_PARTNUM_CP2102N_QFN28 0x20
#define CP210X_PARTNUM_CP2102N_QFN24 0x21
#define CP210X_PARTNUM_CP2102N_QFN20 0x22
#define CP210X_PARTNUM_UNKNOWN 0xFF
#ifdef __cplusplus
extern "C" {
#endif
#ifdef __cplusplus
}
#endif
#endif /* USBH_CP210X_H */

View File

@@ -0,0 +1,407 @@
/*
* Copyright (c) 2024 ~ 2025, sakumisu
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "usbh_core.h"
#include "usbh_serial.h"
#include "usbh_ftdi.h"
#undef USB_DBG_TAG
#define USB_DBG_TAG "usbh_ftdi"
#include "usb_log.h"
enum ftdi_chip_type {
SIO,
FT232A,
FT232B,
FT2232C,
FT232R,
FT232H,
FT2232H,
FT4232H,
FT4232HA,
FT232HP,
FT233HP,
FT2232HP,
FT2233HP,
FT4232HP,
FT4233HP,
FTX,
};
static const char *ftdi_chip_name[] = {
[SIO] = "SIO", /* the serial part of FT8U100AX */
[FT232A] = "FT232A",
[FT232B] = "FT232B",
[FT2232C] = "FT2232C/D",
[FT232R] = "FT232R",
[FT232H] = "FT232H",
[FT2232H] = "FT2232H",
[FT4232H] = "FT4232H",
[FT4232HA] = "FT4232HA",
[FT232HP] = "FT232HP",
[FT233HP] = "FT233HP",
[FT2232HP] = "FT2232HP",
[FT2233HP] = "FT2233HP",
[FT4232HP] = "FT4232HP",
[FT4233HP] = "FT4233HP",
[FTX] = "FT-X",
};
struct usbh_ftdi {
enum ftdi_chip_type chip_type;
};
static uint32_t ftdi_232bm_baud_base_to_divisor(uint32_t baud, int base)
{
static const unsigned char divfrac[8] = { 0, 3, 2, 4, 1, 5, 6, 7 };
uint32_t divisor;
/* divisor shifted 3 bits to the left */
int divisor3 = DIV_ROUND_CLOSEST(base, 2 * baud);
divisor = divisor3 >> 3;
divisor |= (uint32_t)divfrac[divisor3 & 0x7] << 14;
/* Deal with special cases for highest baud rates. */
if (divisor == 1) /* 1.0 */
divisor = 0;
else if (divisor == 0x4001) /* 1.5 */
divisor = 1;
return divisor;
}
static uint32_t ftdi_232bm_baud_to_divisor(uint32_t baud)
{
return ftdi_232bm_baud_base_to_divisor(baud, 48000000);
}
static uint32_t ftdi_2232h_baud_base_to_divisor(uint32_t baud, int base)
{
static const unsigned char divfrac[8] = { 0, 3, 2, 4, 1, 5, 6, 7 };
uint32_t divisor;
int divisor3;
/* hi-speed baud rate is 10-bit sampling instead of 16-bit */
divisor3 = DIV_ROUND_CLOSEST(8 * base, 10 * baud);
divisor = divisor3 >> 3;
divisor |= (uint32_t)divfrac[divisor3 & 0x7] << 14;
/* Deal with special cases for highest baud rates. */
if (divisor == 1) /* 1.0 */
divisor = 0;
else if (divisor == 0x4001) /* 1.5 */
divisor = 1;
/*
* Set this bit to turn off a divide by 2.5 on baud rate generator
* This enables baud rates up to 12Mbaud but cannot reach below 1200
* baud with this bit set
*/
divisor |= 0x00020000;
return divisor;
}
static uint32_t ftdi_2232h_baud_to_divisor(uint32_t baud)
{
return ftdi_2232h_baud_base_to_divisor(baud, 120000000);
}
int usbh_ftdi_reset(struct usbh_serial *serial)
{
struct usb_setup_packet *setup;
if (!serial || !serial->hport) {
return -USB_ERR_INVAL;
}
setup = serial->hport->setup;
setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE;
setup->bRequest = FTDI_SIO_RESET;
setup->wValue = 0;
setup->wIndex = serial->intf;
setup->wLength = 0;
return usbh_control_transfer(serial->hport, setup, NULL);
}
static int usbh_ftdi_set_baudrate(struct usbh_serial *serial, uint32_t baudrate)
{
struct usb_setup_packet *setup;
struct usbh_ftdi *ftdi_class;
uint32_t div_value;
uint16_t value;
uint8_t baudrate_high;
if (!serial || !serial->hport || !serial->priv) {
return -USB_ERR_INVAL;
}
setup = serial->hport->setup;
ftdi_class = (struct usbh_ftdi *)serial->priv;
switch (ftdi_class->chip_type) {
case FT232B:
case FT2232C:
case FT232R:
if (baudrate > 3000000) {
return -USB_ERR_INVAL;
}
div_value = ftdi_232bm_baud_to_divisor(baudrate);
break;
default:
if ((baudrate <= 12000000) && (baudrate >= 1200)) {
div_value = ftdi_2232h_baud_to_divisor(baudrate);
} else if (baudrate < 1200) {
div_value = ftdi_232bm_baud_to_divisor(baudrate);
} else {
return -USB_ERR_INVAL;
}
break;
}
value = div_value & 0xFFFF;
baudrate_high = (div_value >> 16) & 0xff;
setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE;
setup->bRequest = FTDI_SIO_SET_BAUDRATE;
setup->wValue = value;
setup->wIndex = (baudrate_high << 8) | serial->intf;
setup->wLength = 0;
return usbh_control_transfer(serial->hport, setup, NULL);
}
static int usbh_ftdi_set_data_format(struct usbh_serial *serial, uint8_t databits, uint8_t parity, uint8_t stopbits, uint8_t isbreak)
{
/**
* D0-D7 databits BITS_7=7, BITS_8=8
* D8-D10 parity NONE=0, ODD=1, EVEN=2, MARK=3, SPACE=4
* D11-D12 STOP_BIT_1=0, STOP_BIT_15=1, STOP_BIT_2=2
* D14 BREAK_OFF=0, BREAK_ON=1
**/
struct usb_setup_packet *setup;
uint16_t value;
if (!serial || !serial->hport) {
return -USB_ERR_INVAL;
}
setup = serial->hport->setup;
value = ((isbreak & 0x01) << 14) | ((stopbits & 0x03) << 11) | ((parity & 0x0f) << 8) | (databits & 0x0f);
setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE;
setup->bRequest = FTDI_SIO_SET_DATA;
setup->wValue = value;
setup->wIndex = serial->intf;
setup->wLength = 0;
return usbh_control_transfer(serial->hport, setup, NULL);
}
static int usbh_ftdi_set_latency_timer(struct usbh_serial *serial, uint16_t value)
{
struct usb_setup_packet *setup;
if (!serial || !serial->hport) {
return -USB_ERR_INVAL;
}
setup = serial->hport->setup;
setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE;
setup->bRequest = FTDI_SIO_SET_LATENCY_TIMER;
setup->wValue = value;
setup->wIndex = serial->intf;
setup->wLength = 0;
return usbh_control_transfer(serial->hport, setup, NULL);
}
static int usbh_ftdi_attach(struct usbh_serial *serial)
{
uint16_t version;
uint8_t chip_type;
int ret;
version = serial->hport->device_desc.bcdDevice;
switch (version) {
case 0x400:
chip_type = FT232B;
break;
case 0x500:
chip_type = FT2232C;
break;
case 0x600:
chip_type = FT232R;
break;
case 0x700:
chip_type = FT2232H;
break;
case 0x900:
chip_type = FT232H;
break;
default:
USB_LOG_ERR("Unsupported FTDI chip version: 0x%04x\r\n", version);
return -USB_ERR_NOTSUPP;
}
USB_LOG_INFO("chip name: %s\r\n", ftdi_chip_name[chip_type]);
struct usbh_ftdi *ftdi_class = usb_osal_malloc(sizeof(struct usbh_ftdi));
if (!ftdi_class) {
USB_LOG_ERR("No memory for ftdi_class\r\n");
return -USB_ERR_NOMEM;
}
memset(ftdi_class, 0, sizeof(struct usbh_ftdi));
serial->priv = ftdi_class;
ftdi_class->chip_type = chip_type;
ret = usbh_ftdi_set_latency_timer(serial, 0x10);
if (ret < 0) {
goto errout;
}
return 0;
errout:
serial->priv = NULL;
usb_osal_free(ftdi_class);
return ret;
}
static void usbh_ftdi_detach(struct usbh_serial *serial)
{
if (serial && serial->priv) {
serial->priv = NULL;
usb_osal_free(serial->priv);
}
}
static int usbh_ftdi_set_flow_ctrl(struct usbh_serial *serial, bool hardctrl)
{
struct usb_setup_packet *setup;
if (!serial || !serial->hport) {
return -USB_ERR_INVAL;
}
setup = serial->hport->setup;
setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE;
setup->bRequest = FTDI_SIO_SET_FLOW_CTRL;
setup->wValue = hardctrl ? FTDI_SIO_RTS_CTS_HS : FTDI_SIO_DISABLE_FLOW_CTRL;
setup->wIndex = serial->intf;
setup->wLength = 0;
return usbh_control_transfer(serial->hport, setup, NULL);
}
static int usbh_ftdi_set_line_coding(struct usbh_serial *serial, struct cdc_line_coding *line_coding)
{
int ret = usbh_ftdi_set_baudrate(serial, line_coding->dwDTERate);
if (ret < 0) {
return ret;
}
return usbh_ftdi_set_data_format(serial, line_coding->bDataBits, line_coding->bParityType, line_coding->bCharFormat, 0);
}
static int usbh_ftdi_set_line_state(struct usbh_serial *serial, bool dtr, bool rts)
{
struct usb_setup_packet *setup;
uint16_t value = 0;
if (!serial || !serial->hport) {
return -USB_ERR_INVAL;
}
setup = serial->hport->setup;
value = ((dtr ? FTDI_SIO_SET_DTR_HIGH : FTDI_SIO_SET_DTR_LOW) | (rts ? FTDI_SIO_SET_RTS_HIGH : FTDI_SIO_SET_RTS_LOW));
setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE;
setup->bRequest = FTDI_SIO_SET_MODEM_CTRL;
setup->wValue = value;
setup->wIndex = serial->intf;
setup->wLength = 0;
return usbh_control_transfer(serial->hport, setup, NULL);
}
static int usbh_ftdi_get_modem_status(struct usbh_serial *serial)
{
struct usb_setup_packet *setup;
uint16_t status = 0;
int ret;
if (!serial || !serial->hport) {
return -USB_ERR_INVAL;
}
setup = serial->hport->setup;
setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE;
setup->bRequest = FTDI_SIO_GET_MODEM_STATUS;
setup->wValue = 0x0000;
setup->wIndex = serial->intf;
setup->wLength = 2;
ret = usbh_control_transfer(serial->hport, setup, serial->iobuffer);
if (ret < 0) {
return 0;
}
status = (serial->iobuffer[0] & FTDI_SIO_DSR_MASK ? USBH_SERIAL_TIOCM_DSR : 0) |
(serial->iobuffer[0] & FTDI_SIO_CTS_MASK ? USBH_SERIAL_TIOCM_CTS : 0) |
(serial->iobuffer[0] & FTDI_SIO_RI_MASK ? USBH_SERIAL_TIOCM_RI : 0) |
(serial->iobuffer[0] & FTDI_SIO_RLSD_MASK ? USBH_SERIAL_TIOCM_CD : 0) |
(serial->line_state & USBH_SERIAL_TIOCM_DTR ? USBH_SERIAL_TIOCM_DTR : 0) |
(serial->line_state & USBH_SERIAL_TIOCM_RTS ? USBH_SERIAL_TIOCM_RTS : 0);
return status;
}
static const struct usbh_serial_driver ftdi_driver = {
.driver_name = "ftdi",
.ignore_rx_header = 2,
.ignore_tx_header = 0,
.attach = usbh_ftdi_attach,
.detach = usbh_ftdi_detach,
.set_flow_control = usbh_ftdi_set_flow_ctrl,
.set_line_coding = usbh_ftdi_set_line_coding,
.get_line_coding = NULL,
.set_line_state = usbh_ftdi_set_line_state,
.get_modem_status = usbh_ftdi_get_modem_status,
};
static int usbh_ftdi_connect(struct usbh_hubport *hport, uint8_t intf)
{
return usbh_serial_probe(hport, intf, &ftdi_driver) ? 0 : -USB_ERR_NOMEM;
}
static int usbh_ftdi_disconnect(struct usbh_hubport *hport, uint8_t intf)
{
struct usbh_serial *serial = (struct usbh_serial *)hport->config.intf[intf].priv;
if (serial) {
usbh_serial_remove(serial);
}
return 0;
}
static const uint16_t ftdi_id_table[][2] = {
{ 0x0403, 0x6001 },
{ 0x0403, 0x6010 },
{ 0x0403, 0x6014 },
{ 0, 0 },
};
const struct usbh_class_driver ftdi_class_driver = {
.driver_name = "ftdi",
.connect = usbh_ftdi_connect,
.disconnect = usbh_ftdi_disconnect
};
CLASS_INFO_DEFINE const struct usbh_class_info ftdi_class_info = {
.match_flags = USB_CLASS_MATCH_VID_PID | USB_CLASS_MATCH_INTF_CLASS,
.bInterfaceClass = 0xff,
.bInterfaceSubClass = 0x00,
.bInterfaceProtocol = 0x00,
.id_table = ftdi_id_table,
.class_driver = &ftdi_class_driver
};

View File

@@ -0,0 +1,341 @@
/*
* Copyright (c) 2024 ~ 2025, sakumisu
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef USBH_FTDI_H
#define USBH_FTDI_H
#include "usb_cdc.h"
#define FTDI_VID 0x0403 /* Vendor Id */
/* FTDI device PIDs */
#define FTDI_8U232AM_PID 0x6001 /* Similar device to SIO above */
#define FTDI_8U232AM_ALT_PID 0x6006 /* FTDI's alternate PID for above */
#define FTDI_8U2232C_PID 0x6010 /* Dual channel device */
#define FTDI_4232H_PID 0x6011 /* Quad channel hi-speed device */
#define FTDI_232H_PID 0x6014 /* Single channel hi-speed device */
#define FTDI_FTX_PID 0x6015 /* FT-X series (FT201X, FT230X, FT231X, etc) */
#define FTDI_FT2233HP_PID 0x6040 /* Dual channel hi-speed device with PD */
#define FTDI_FT4233HP_PID 0x6041 /* Quad channel hi-speed device with PD */
#define FTDI_FT2232HP_PID 0x6042 /* Dual channel hi-speed device with PD */
#define FTDI_FT4232HP_PID 0x6043 /* Quad channel hi-speed device with PD */
#define FTDI_FT233HP_PID 0x6044 /* Dual channel hi-speed device with PD */
#define FTDI_FT232HP_PID 0x6045 /* Dual channel hi-speed device with PD */
#define FTDI_FT4232HA_PID 0x6048 /* Quad channel automotive grade hi-speed device */
#define FTDI_SIO_PID 0x8372 /* Product Id SIO application of 8U100AX */
#define FTDI_232RL_PID 0xFBFA /* Product ID for FT232RL */
/* Requests */
#define FTDI_SIO_RESET 0x00 /* Reset the port */
#define FTDI_SIO_SET_MODEM_CTRL 0x01 /* Set the modem control register */
#define FTDI_SIO_SET_FLOW_CTRL 0x02 /* Set flow control register */
#define FTDI_SIO_SET_BAUDRATE 0x03 /* Set baud rate */
#define FTDI_SIO_SET_DATA 0x04 /* Set the data characteristics of the port */
#define FTDI_SIO_GET_MODEM_STATUS 0x05
#define FTDI_SIO_SET_EVENT_CHAR 0x06
#define FTDI_SIO_SET_ERROR_CHAR 0x07
#define FTDI_SIO_SET_LATENCY_TIMER 0x09
#define FTDI_SIO_GET_LATENCY_TIMER 0x0A
#define FTDI_SIO_SET_BITMODE 0x0B
#define FTDI_SIO_READ_PINS 0x0C
#define FTDI_SIO_READ_EEPROM 0x90
#define FTDI_SIO_WRITE_EEPROM 0x91
#define FTDI_SIO_ERASE_EEPROM 0x92
/* Channel indices for FT2232, FT2232H and FT4232H devices */
#define FTDI_SIO_CHANNEL_A 1
#define FTDI_SIO_CHANNEL_B 2
#define FTDI_SIO_CHANNEL_C 3
#define FTDI_SIO_CHANNEL_D 4
/*
* BmRequestType: 0100 0000B
* bRequest: FTDI_SIO_RESET
* wValue: Control Value
* 0 = Reset SIO
* 1 = Purge RX buffer
* 2 = Purge TX buffer
* wIndex: Port
* wLength: 0
* Data: None
*
* The Reset SIO command has this effect:
*
* Sets flow control set to 'none'
* Event char = $0D
* Event trigger = disabled
* Purge RX buffer
* Purge TX buffer
* Clear DTR
* Clear RTS
* baud and data format not reset
*
* The Purge RX and TX buffer commands affect nothing except the buffers
*
*/
#define FTDI_SIO_RESET_SIO 0
#define FTDI_SIO_RESET_PURGE_RX 1
#define FTDI_SIO_RESET_PURGE_TX 2
/*
* BmRequestType: 0100 0000B
* bRequest: FTDI_SIO_SET_BAUDRATE
* wValue: BaudDivisor value - see below
* wIndex: Port
* wLength: 0
* Data: None
* The BaudDivisor values are calculated as follows:
* - BaseClock is either 12000000 or 48000000 depending on the device.
* FIXME: I wish I knew how to detect old chips to select proper base clock!
* - BaudDivisor is a fixed point number encoded in a funny way.
* (--WRONG WAY OF THINKING--)
* BaudDivisor is a fixed point number encoded with following bit weighs:
* (-2)(-1)(13..0). It is a radical with a denominator of 4, so values
* end with 0.0 (00...), 0.25 (10...), 0.5 (01...), and 0.75 (11...).
* (--THE REALITY--)
* The both-bits-set has quite different meaning from 0.75 - the chip
* designers have decided it to mean 0.125 instead of 0.75.
* This info looked up in FTDI application note "FT8U232 DEVICES \ Data Rates
* and Flow Control Consideration for USB to RS232".
* - BaudDivisor = (BaseClock / 16) / BaudRate, where the (=) operation should
* automagically re-encode the resulting value to take fractions into
* consideration.
* As all values are integers, some bit twiddling is in order:
* BaudDivisor = (BaseClock / 16 / BaudRate) |
* (((BaseClock / 2 / BaudRate) & 4) ? 0x4000 // 0.5
* : ((BaseClock / 2 / BaudRate) & 2) ? 0x8000 // 0.25
* : ((BaseClock / 2 / BaudRate) & 1) ? 0xc000 // 0.125
* : 0)
*
* For the FT232BM, a 17th divisor bit was introduced to encode the multiples
* of 0.125 missing from the FT8U232AM. Bits 16 to 14 are coded as follows
* (the first four codes are the same as for the FT8U232AM, where bit 16 is
* always 0):
* 000 - add .000 to divisor
* 001 - add .500 to divisor
* 010 - add .250 to divisor
* 011 - add .125 to divisor
* 100 - add .375 to divisor
* 101 - add .625 to divisor
* 110 - add .750 to divisor
* 111 - add .875 to divisor
* Bits 15 to 0 of the 17-bit divisor are placed in the urb value. Bit 16 is
* placed in bit 0 of the urb index.
*
* Note that there are a couple of special cases to support the highest baud
* rates. If the calculated divisor value is 1, this needs to be replaced with
* 0. Additionally for the FT232BM, if the calculated divisor value is 0x4001
* (1.5), this needs to be replaced with 0x0001 (1) (but this divisor value is
* not supported by the FT8U232AM).
*/
enum ftdi_sio_baudrate {
ftdi_sio_b300 = 0,
ftdi_sio_b600 = 1,
ftdi_sio_b1200 = 2,
ftdi_sio_b2400 = 3,
ftdi_sio_b4800 = 4,
ftdi_sio_b9600 = 5,
ftdi_sio_b19200 = 6,
ftdi_sio_b38400 = 7,
ftdi_sio_b57600 = 8,
ftdi_sio_b115200 = 9
};
/*
* BmRequestType: 0100 0000B
* bRequest: FTDI_SIO_SET_DATA
* wValue: Data characteristics (see below)
* wIndex: Port
* wLength: 0
* Data: No
*
* Data characteristics
*
* B0..7 Number of data bits
* B8..10 Parity
* 0 = None
* 1 = Odd
* 2 = Even
* 3 = Mark
* 4 = Space
* B11..13 Stop Bits
* 0 = 1
* 1 = 1.5
* 2 = 2
* B14
* 1 = TX ON (break)
* 0 = TX OFF (normal state)
* B15 Reserved
*
*/
#define FTDI_SIO_SET_DATA_PARITY_NONE (0x0 << 8)
#define FTDI_SIO_SET_DATA_PARITY_ODD (0x1 << 8)
#define FTDI_SIO_SET_DATA_PARITY_EVEN (0x2 << 8)
#define FTDI_SIO_SET_DATA_PARITY_MARK (0x3 << 8)
#define FTDI_SIO_SET_DATA_PARITY_SPACE (0x4 << 8)
#define FTDI_SIO_SET_DATA_STOP_BITS_1 (0x0 << 11)
#define FTDI_SIO_SET_DATA_STOP_BITS_15 (0x1 << 11)
#define FTDI_SIO_SET_DATA_STOP_BITS_2 (0x2 << 11)
#define FTDI_SIO_SET_BREAK (0x1 << 14)
/*
* BmRequestType: 0100 0000B
* bRequest: FTDI_SIO_MODEM_CTRL
* wValue: ControlValue (see below)
* wIndex: Port
* wLength: 0
* Data: None
*
* NOTE: If the device is in RTS/CTS flow control, the RTS set by this
* command will be IGNORED without an error being returned
* Also - you can not set DTR and RTS with one control message
*
* ControlValue
* B0 DTR state
* 0 = reset
* 1 = set
* B1 RTS state
* 0 = reset
* 1 = set
* B2..7 Reserved
* B8 DTR state enable
* 0 = ignore
* 1 = use DTR state
* B9 RTS state enable
* 0 = ignore
* 1 = use RTS state
* B10..15 Reserved
*
*/
#define FTDI_SIO_SET_DTR_MASK 0x1
#define FTDI_SIO_SET_DTR_HIGH ((FTDI_SIO_SET_DTR_MASK << 8) | 1)
#define FTDI_SIO_SET_DTR_LOW ((FTDI_SIO_SET_DTR_MASK << 8) | 0)
#define FTDI_SIO_SET_RTS_MASK 0x2
#define FTDI_SIO_SET_RTS_HIGH ((FTDI_SIO_SET_RTS_MASK << 8) | 2)
#define FTDI_SIO_SET_RTS_LOW ((FTDI_SIO_SET_RTS_MASK << 8) | 0)
/*
* BmRequestType: 0100 0000b
* bRequest: FTDI_SIO_SET_FLOW_CTRL
* wValue: Xoff/Xon
* wIndex: Protocol/Port - hIndex is protocol / lIndex is port
* wLength: 0
* Data: None
*
* hIndex protocol is:
* B0 Output handshaking using RTS/CTS
* 0 = disabled
* 1 = enabled
* B1 Output handshaking using DTR/DSR
* 0 = disabled
* 1 = enabled
* B2 Xon/Xoff handshaking
* 0 = disabled
* 1 = enabled
*
* A value of zero in the hIndex field disables handshaking
*
* If Xon/Xoff handshaking is specified, the hValue field should contain the
* XOFF character and the lValue field contains the XON character.
*/
#define FTDI_SIO_DISABLE_FLOW_CTRL 0x0
#define FTDI_SIO_RTS_CTS_HS (0x1 << 8)
#define FTDI_SIO_DTR_DSR_HS (0x2 << 8)
#define FTDI_SIO_XON_XOFF_HS (0x4 << 8)
/*
* BmRequestType: 1100 0000b
* bRequest: FTDI_SIO_GET_MODEM_STATUS
* wValue: zero
* wIndex: Port
* wLength: 1
* Data: Status
*
* One byte of data is returned
* B0..3 0
* B4 CTS
* 0 = inactive
* 1 = active
* B5 DSR
* 0 = inactive
* 1 = active
* B6 Ring Indicator (RI)
* 0 = inactive
* 1 = active
* B7 Receive Line Signal Detect (RLSD)
* 0 = inactive
* 1 = active
*/
#define FTDI_SIO_CTS_MASK 0x10
#define FTDI_SIO_DSR_MASK 0x20
#define FTDI_SIO_RI_MASK 0x40
#define FTDI_SIO_RLSD_MASK 0x80
/* Possible bitmodes for FTDI_SIO_SET_BITMODE_REQUEST */
#define FTDI_SIO_BITMODE_RESET 0x00
#define FTDI_SIO_BITMODE_CBUS 0x20
/*
* IN Endpoint
*
* The device reserves the first two bytes of data on this endpoint to contain
* the current values of the modem and line status registers. In the absence of
* data, the device generates a message consisting of these two status bytes
* every 40 ms
*
* Byte 0: Modem Status
*
* Offset Description
* B0 Reserved - must be 1
* B1 Reserved - must be 0
* B2 Reserved - must be 0
* B3 Reserved - must be 0
* B4 Clear to Send (CTS)
* B5 Data Set Ready (DSR)
* B6 Ring Indicator (RI)
* B7 Receive Line Signal Detect (RLSD)
*
* Byte 1: Line Status
*
* Offset Description
* B0 Data Ready (DR)
* B1 Overrun Error (OE)
* B2 Parity Error (PE)
* B3 Framing Error (FE)
* B4 Break Interrupt (BI)
* B5 Transmitter Holding Register (THRE)
* B6 Transmitter Empty (TEMT)
* B7 Error in RCVR FIFO
*
*/
#define FTDI_RS0_CTS (1 << 4)
#define FTDI_RS0_DSR (1 << 5)
#define FTDI_RS0_RI (1 << 6)
#define FTDI_RS0_RLSD (1 << 7)
#define FTDI_RS_DR 1
#define FTDI_RS_OE (1 << 1)
#define FTDI_RS_PE (1 << 2)
#define FTDI_RS_FE (1 << 3)
#define FTDI_RS_BI (1 << 4)
#define FTDI_RS_THRE (1 << 5)
#define FTDI_RS_TEMT (1 << 6)
#define FTDI_RS_FIFO (1 << 7)
#ifdef __cplusplus
extern "C" {
#endif
#ifdef __cplusplus
}
#endif
#endif /* USBH_FTDI_H */

View File

@@ -0,0 +1,131 @@
/*
* 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;
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_WRN("Do not find interrupt endpoint, so disable modem status monitor\r\n");
}
return 0;
}
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 int usbh_gsm_set_line_coding(struct usbh_serial *serial, struct cdc_line_coding *line_coding)
{
return 0;
}
static int usbh_gsm_set_line_state(struct usbh_serial *serial, bool dtr, bool rts)
{
return 0;
}
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 = usbh_gsm_set_line_coding,
.get_line_coding = NULL,
.set_line_state = usbh_gsm_set_line_state,
.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 */
{ 0x2ECC, 0x3012 }, /* Chinamobile ML307R */
{ 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
};

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,43 @@
/*
* Copyright (c) 2024 ~ 2025, sakumisu
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef USBH_PL2303_H
#define USBH_PL2303_H
#include "usb_cdc.h"
#define PL2303_VENDOR_WRITE_REQUEST 0x01
#define PL2303_VENDOR_WRITE_NREQUEST 0x80
#define PL2303_VENDOR_READ_REQUEST 0x01
#define PL2303_VENDOR_READ_NREQUEST 0x81
#define PL2303_FLOWCTRL_MASK 0xf0
#define PL2303_READ_TYPE_HX_STATUS 0x8080
#define PL2303_HXN_RESET_REG 0x07
#define PL2303_HXN_RESET_UPSTREAM_PIPE 0x02
#define PL2303_HXN_RESET_DOWNSTREAM_PIPE 0x01
#define PL2303_HXN_FLOWCTRL_REG 0x0a
#define PL2303_HXN_FLOWCTRL_MASK 0x1c
#define PL2303_HXN_FLOWCTRL_NONE 0x1c
#define PL2303_HXN_FLOWCTRL_RTS_CTS 0x18
#define PL2303_HXN_FLOWCTRL_XON_XOFF 0x0c
#define PL2303_QUIRK_UART_STATE_IDX0 BIT(0)
#define PL2303_QUIRK_LEGACY BIT(1)
#define PL2303_QUIRK_ENDPOINT_HACK BIT(2)
#define PL2303_QUIRK_NO_BREAK_GETLINE BIT(3)
#ifdef __cplusplus
extern "C" {
#endif
#ifdef __cplusplus
}
#endif
#endif /* USBH_PL2303_H */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,182 @@
/*
* 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_OFFSET 0
#define USBH_SERIAL_CTRL_NOCACHE_SIZE 32
#define USBH_SERIAL_INT_NOCACHE_OFFSET USB_ALIGN_UP(USBH_SERIAL_CTRL_NOCACHE_SIZE, CONFIG_USB_ALIGN_SIZE)
#define USBH_SERIAL_INT_NOCACHE_SIZE 32
#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_RX2_NOCACHE_OFFSET USB_ALIGN_UP((USBH_SERIAL_RX_NOCACHE_OFFSET + USBH_SERIAL_RX_NOCACHE_SIZE), CONFIG_USB_ALIGN_SIZE)
#define USBH_SERIAL_RX2_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;
uint8_t rx_buf_index;
int rx_errorcode;
usbh_serial_rx_complete_callback_t rx_complete_callback;
void *priv; /* Private Data */
void *user_data; /* User Data */
};
/* internal api */
struct usbh_serial *usbh_serial_probe(struct usbh_hubport *hport, uint8_t intf, const struct usbh_serial_driver *driver);
void usbh_serial_remove(struct usbh_serial *serial);
/* public api */
struct usbh_serial *usbh_serial_open(const char *devname, uint32_t open_flags);
int usbh_serial_close(struct usbh_serial *serial);
int usbh_serial_control(struct usbh_serial *serial, int cmd, void *arg);
int usbh_serial_write(struct usbh_serial *serial, const void *buffer, uint32_t buflen);
int usbh_serial_read(struct usbh_serial *serial, void *buffer, uint32_t buflen);
/* cdc only api */
int usbh_serial_cdc_write_async(struct usbh_serial *serial, uint8_t *buffer, uint32_t buflen, usbh_complete_callback_t complete, void *arg);
int usbh_serial_cdc_read_async(struct usbh_serial *serial, uint8_t *buffer, uint32_t buflen, usbh_complete_callback_t complete, void *arg);
/* public weak api */
void usbh_serial_run(struct usbh_serial *serial);
void usbh_serial_stop(struct usbh_serial *serial);
int usbh_serial(int argc, char **argv);
#ifdef __cplusplus
}
#endif
#endif /* USBH_SERIAL_H */

View File

@@ -795,16 +795,6 @@ int usbh_asix_eth_output(uint32_t buflen)
return usbh_submit_urb(&g_asix_class.bulkout_urb);
}
__WEAK void usbh_asix_run(struct usbh_asix *asix_class)
{
(void)asix_class;
}
__WEAK void usbh_asix_stop(struct usbh_asix *asix_class)
{
(void)asix_class;
}
static const uint16_t asix_id_table[][2] = {
{ 0x0B95, 0x772B },
{ 0x0B95, 0x7720 },

View File

@@ -1034,17 +1034,17 @@ static int generic_ocp_read(struct usbh_rtl8152 *tp, uint16_t index, uint16_t si
static int generic_ocp_write(struct usbh_rtl8152 *tp, uint16_t index, uint16_t byteen,
uint16_t size, void *data, uint16_t type)
{
int ret;
int ret = -USB_ERR_INVAL;
uint16_t byteen_start, byteen_end, byen;
uint16_t limit = 512;
uint8_t *buf = data;
/* both size and indix must be 4 bytes align */
if ((size & 3) || !size || (index & 3) || !buf)
return -USB_ERR_INVAL;
return ret;
if ((uint32_t)index + (uint32_t)size > 0xffff)
return -USB_ERR_INVAL;
return ret;
byteen_start = byteen & BYTE_EN_START_MASK;
byteen_end = byteen & BYTE_EN_END_MASK;
@@ -1596,8 +1596,8 @@ static void r8153_teredo_off(struct usbh_rtl8152 *tp)
case RTL_VER_15:
default:
/* The bit 0 ~ 7 are relative with teredo settings. They are
* W1C (write 1 to clear), so set all 1 to disable it.
*/
* W1C (write 1 to clear), so set all 1 to disable it.
*/
ocp_write_byte(tp, MCU_TYPE_PLA, PLA_TEREDO_CFG, 0xff);
break;
}
@@ -2251,16 +2251,6 @@ int usbh_rtl8152_eth_output(uint32_t buflen)
return usbh_submit_urb(&g_rtl8152_class.bulkout_urb);
}
__WEAK void usbh_rtl8152_run(struct usbh_rtl8152 *rtl8152_class)
{
(void)rtl8152_class;
}
__WEAK void usbh_rtl8152_stop(struct usbh_rtl8152 *rtl8152_class)
{
(void)rtl8152_class;
}
static const uint16_t rtl_id_table[][2] = {
{ 0x0BDA, 0x8152 },
{ 0, 0 },

View File

@@ -1,379 +0,0 @@
/*
* Copyright (c) 2024, sakumisu
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "usbh_core.h"
#include "usbh_ch34x.h"
#define DEV_FORMAT "/dev/ttyUSB%d"
USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_ch34x_buf[USB_ALIGN_UP(64, CONFIG_USB_ALIGN_SIZE)];
#define CONFIG_USBHOST_MAX_CP210X_CLASS 1
static struct usbh_ch34x g_ch34x_class[CONFIG_USBHOST_MAX_CP210X_CLASS];
static uint32_t g_devinuse = 0;
static struct usbh_ch34x *usbh_ch34x_class_alloc(void)
{
uint8_t devno;
for (devno = 0; devno < CONFIG_USBHOST_MAX_CP210X_CLASS; devno++) {
if ((g_devinuse & (1U << devno)) == 0) {
g_devinuse |= (1U << devno);
memset(&g_ch34x_class[devno], 0, sizeof(struct usbh_ch34x));
g_ch34x_class[devno].minor = devno;
return &g_ch34x_class[devno];
}
}
return NULL;
}
static void usbh_ch34x_class_free(struct usbh_ch34x *ch34x_class)
{
uint8_t devno = ch34x_class->minor;
if (devno < 32) {
g_devinuse &= ~(1U << devno);
}
memset(ch34x_class, 0, sizeof(struct usbh_ch34x));
}
static int usbh_ch34x_get_baudrate_div(uint32_t baudrate, uint8_t *factor, uint8_t *divisor)
{
uint8_t a;
uint8_t b;
uint32_t c;
switch (baudrate) {
case 921600:
a = 0xf3;
b = 7;
break;
case 307200:
a = 0xd9;
b = 7;
break;
default:
if (baudrate > 6000000 / 255) {
b = 3;
c = 6000000;
} else if (baudrate > 750000 / 255) {
b = 2;
c = 750000;
} else if (baudrate > 93750 / 255) {
b = 1;
c = 93750;
} else {
b = 0;
c = 11719;
}
a = (uint8_t)(c / baudrate);
if (a == 0 || a == 0xFF) {
return -USB_ERR_INVAL;
}
if ((c / a - baudrate) > (baudrate - c / (a + 1))) {
a++;
}
a = (uint8_t)(256 - a);
break;
}
*factor = a;
*divisor = b;
return 0;
}
static int usbh_ch34x_get_version(struct usbh_ch34x *ch34x_class)
{
struct usb_setup_packet *setup;
int ret;
if (!ch34x_class || !ch34x_class->hport) {
return -USB_ERR_INVAL;
}
setup = ch34x_class->hport->setup;
setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE;
setup->bRequest = CH34X_READ_VERSION;
setup->wValue = 0;
setup->wIndex = 0;
setup->wLength = 2;
ret = usbh_control_transfer(ch34x_class->hport, setup, g_ch34x_buf);
if (ret < 0) {
return ret;
}
USB_LOG_INFO("Ch34x chip version %02x:%02x\r\n", g_ch34x_buf[0], g_ch34x_buf[1]);
return ret;
}
static int usbh_ch34x_flow_ctrl(struct usbh_ch34x *ch34x_class)
{
struct usb_setup_packet *setup;
if (!ch34x_class || !ch34x_class->hport) {
return -USB_ERR_INVAL;
}
setup = ch34x_class->hport->setup;
setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE;
setup->bRequest = CH34X_WRITE_REG;
setup->wValue = 0x2727;
setup->wIndex = 0;
setup->wLength = 0;
return usbh_control_transfer(ch34x_class->hport, setup, NULL);
}
int usbh_ch34x_set_line_coding(struct usbh_ch34x *ch34x_class, struct cdc_line_coding *line_coding)
{
struct usb_setup_packet *setup;
uint16_t reg_value = 0;
uint16_t value = 0;
uint8_t factor = 0;
uint8_t divisor = 0;
if (!ch34x_class || !ch34x_class->hport) {
return -USB_ERR_INVAL;
}
setup = ch34x_class->hport->setup;
memcpy((uint8_t *)&ch34x_class->line_coding, line_coding, sizeof(struct cdc_line_coding));
/* refer to https://github.com/WCHSoftGroup/ch341ser_linux/blob/main/driver/ch341.c */
switch (line_coding->bParityType) {
case 0:
break;
case 1:
reg_value |= CH341_L_PO;
break;
case 2:
reg_value |= CH341_L_PE;
break;
case 3:
reg_value |= CH341_L_PM;
break;
case 4:
reg_value |= CH341_L_PS;
break;
default:
return -USB_ERR_INVAL;
}
switch (line_coding->bDataBits) {
case 5:
reg_value |= CH341_L_D5;
break;
case 6:
reg_value |= CH341_L_D6;
break;
case 7:
reg_value |= CH341_L_D7;
break;
case 8:
reg_value |= CH341_L_D8;
break;
default:
return -USB_ERR_INVAL;
}
if (line_coding->bCharFormat == 2) {
reg_value |= CH341_L_SB;
}
reg_value |= 0xC0;
value |= 0x9c;
value |= reg_value << 8;
usbh_ch34x_get_baudrate_div(line_coding->dwDTERate, &factor, &divisor);
setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE;
setup->bRequest = CH34X_SERIAL_INIT;
setup->wValue = value;
setup->wIndex = (factor << 8) | 0x80 | divisor;
setup->wLength = 0;
return usbh_control_transfer(ch34x_class->hport, setup, NULL);
}
int usbh_ch34x_get_line_coding(struct usbh_ch34x *ch34x_class, struct cdc_line_coding *line_coding)
{
memcpy(line_coding, (uint8_t *)&ch34x_class->line_coding, sizeof(struct cdc_line_coding));
return 0;
}
int usbh_ch34x_set_line_state(struct usbh_ch34x *ch34x_class, bool dtr, bool rts)
{
struct usb_setup_packet *setup;
if (!ch34x_class || !ch34x_class->hport) {
return -USB_ERR_INVAL;
}
setup = ch34x_class->hport->setup;
setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE;
setup->bRequest = CH34X_MODEM_CTRL;
setup->wValue = 0x0f | (dtr << 5) | (rts << 6);
setup->wIndex = 0;
setup->wLength = 0;
return usbh_control_transfer(ch34x_class->hport, setup, NULL);
}
static int usbh_ch34x_connect(struct usbh_hubport *hport, uint8_t intf)
{
struct usb_endpoint_descriptor *ep_desc;
int ret = 0;
struct usbh_ch34x *ch34x_class = usbh_ch34x_class_alloc();
if (ch34x_class == NULL) {
USB_LOG_ERR("Fail to alloc ch34x_class\r\n");
return -USB_ERR_NOMEM;
}
ch34x_class->hport = hport;
ch34x_class->intf = intf;
hport->config.intf[intf].priv = ch34x_class;
usbh_ch34x_get_version(ch34x_class);
usbh_ch34x_flow_ctrl(ch34x_class);
for (uint8_t i = 0; i < hport->config.intf[intf].altsetting[0].intf_desc.bNumEndpoints; i++) {
ep_desc = &hport->config.intf[intf].altsetting[0].ep[i].ep_desc;
if (USB_GET_ENDPOINT_TYPE(ep_desc->bmAttributes) == USB_ENDPOINT_TYPE_INTERRUPT) {
continue;
} else {
if (ep_desc->bEndpointAddress & 0x80) {
USBH_EP_INIT(ch34x_class->bulkin, ep_desc);
} else {
USBH_EP_INIT(ch34x_class->bulkout, ep_desc);
}
}
}
snprintf(hport->config.intf[intf].devname, CONFIG_USBHOST_DEV_NAMELEN, DEV_FORMAT, ch34x_class->minor);
USB_LOG_INFO("Register CH34X Class:%s\r\n", hport->config.intf[intf].devname);
#if 0
USB_LOG_INFO("Test ch34x rx and tx and rx for 5 times, baudrate is 115200\r\n");
struct cdc_line_coding linecoding;
uint8_t count = 5;
linecoding.dwDTERate = 115200;
linecoding.bDataBits = 8;
linecoding.bParityType = 0;
linecoding.bCharFormat = 0;
usbh_ch34x_set_line_coding(ch34x_class, &linecoding);
usbh_ch34x_set_line_state(ch34x_class, true, false);
memset(g_ch34x_buf, 'a', sizeof(g_ch34x_buf));
ret = usbh_ch34x_bulk_out_transfer(ch34x_class, g_ch34x_buf, sizeof(g_ch34x_buf), 0xfffffff);
USB_LOG_RAW("out ret:%d\r\n", ret);
while (count--) {
ret = usbh_ch34x_bulk_in_transfer(ch34x_class, g_ch34x_buf, sizeof(g_ch34x_buf), 0xfffffff);
USB_LOG_RAW("in ret:%d\r\n", ret);
if (ret > 0) {
for (uint32_t i = 0; i < ret; i++) {
USB_LOG_RAW("%02x ", g_ch34x_buf[i]);
}
USB_LOG_RAW("\r\n");
}
}
#endif
usbh_ch34x_run(ch34x_class);
return ret;
}
static int usbh_ch34x_disconnect(struct usbh_hubport *hport, uint8_t intf)
{
int ret = 0;
struct usbh_ch34x *ch34x_class = (struct usbh_ch34x *)hport->config.intf[intf].priv;
if (ch34x_class) {
if (ch34x_class->bulkin) {
usbh_kill_urb(&ch34x_class->bulkin_urb);
}
if (ch34x_class->bulkout) {
usbh_kill_urb(&ch34x_class->bulkout_urb);
}
if (hport->config.intf[intf].devname[0] != '\0') {
usb_osal_thread_schedule_other();
USB_LOG_INFO("Unregister CH34X Class:%s\r\n", hport->config.intf[intf].devname);
usbh_ch34x_stop(ch34x_class);
}
usbh_ch34x_class_free(ch34x_class);
}
return ret;
}
int usbh_ch34x_bulk_in_transfer(struct usbh_ch34x *ch34x_class, uint8_t *buffer, uint32_t buflen, uint32_t timeout)
{
int ret;
struct usbh_urb *urb = &ch34x_class->bulkin_urb;
usbh_bulk_urb_fill(urb, ch34x_class->hport, ch34x_class->bulkin, buffer, buflen, timeout, NULL, NULL);
ret = usbh_submit_urb(urb);
if (ret == 0) {
ret = urb->actual_length;
}
return ret;
}
int usbh_ch34x_bulk_out_transfer(struct usbh_ch34x *ch34x_class, uint8_t *buffer, uint32_t buflen, uint32_t timeout)
{
int ret;
struct usbh_urb *urb = &ch34x_class->bulkout_urb;
usbh_bulk_urb_fill(urb, ch34x_class->hport, ch34x_class->bulkout, buffer, buflen, timeout, NULL, NULL);
ret = usbh_submit_urb(urb);
if (ret == 0) {
ret = urb->actual_length;
}
return ret;
}
__WEAK void usbh_ch34x_run(struct usbh_ch34x *ch34x_class)
{
(void)ch34x_class;
}
__WEAK void usbh_ch34x_stop(struct usbh_ch34x *ch34x_class)
{
(void)ch34x_class;
}
static const uint16_t ch34x_id_table[][2] = {
{ 0x1A86, 0x7523 },
{ 0, 0 },
};
const struct usbh_class_driver ch34x_class_driver = {
.driver_name = "ch34x",
.connect = usbh_ch34x_connect,
.disconnect = usbh_ch34x_disconnect
};
CLASS_INFO_DEFINE const struct usbh_class_info ch34x_class_info = {
.match_flags = USB_CLASS_MATCH_VID_PID | USB_CLASS_MATCH_INTF_CLASS,
.bInterfaceClass = 0xff,
.bInterfaceSubClass = 0x00,
.bInterfaceProtocol = 0x00,
.id_table = ch34x_id_table,
.class_driver = &ch34x_class_driver
};

View File

@@ -1,76 +0,0 @@
/*
* Copyright (c) 2024, sakumisu
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef USBH_CH34X_H
#define USBH_CH34X_H
#include "usb_cdc.h"
/* Requests */
#define CH34X_READ_VERSION 0x5F
#define CH34X_WRITE_REG 0x9A
#define CH34X_READ_REG 0x95
#define CH34X_SERIAL_INIT 0xA1
#define CH34X_MODEM_CTRL 0xA4
// modem control bits
#define CH34X_BIT_RTS (1 << 6)
#define CH34X_BIT_DTR (1 << 5)
#define CH341_CTO_O 0x10
#define CH341_CTO_D 0x20
#define CH341_CTO_R 0x40
#define CH341_CTI_C 0x01
#define CH341_CTI_DS 0x02
#define CH341_CTRL_RI 0x04
#define CH341_CTI_DC 0x08
#define CH341_CTI_ST 0x0f
#define CH341_L_ER 0x80
#define CH341_L_ET 0x40
#define CH341_L_PS 0x38
#define CH341_L_PM 0x28
#define CH341_L_PE 0x18
#define CH341_L_PO 0x08
#define CH341_L_SB 0x04
#define CH341_L_D8 0x03
#define CH341_L_D7 0x02
#define CH341_L_D6 0x01
#define CH341_L_D5 0x00
struct usbh_ch34x {
struct usbh_hubport *hport;
struct usb_endpoint_descriptor *bulkin; /* Bulk IN endpoint */
struct usb_endpoint_descriptor *bulkout; /* Bulk OUT endpoint */
struct usbh_urb bulkout_urb;
struct usbh_urb bulkin_urb;
struct cdc_line_coding line_coding;
uint8_t intf;
uint8_t minor;
void *user_data;
};
#ifdef __cplusplus
extern "C" {
#endif
int usbh_ch34x_set_line_coding(struct usbh_ch34x *ch34x_class, struct cdc_line_coding *line_coding);
int usbh_ch34x_get_line_coding(struct usbh_ch34x *ch34x_class, struct cdc_line_coding *line_coding);
int usbh_ch34x_set_line_state(struct usbh_ch34x *ch34x_class, bool dtr, bool rts);
int usbh_ch34x_bulk_in_transfer(struct usbh_ch34x *ch34x_class, uint8_t *buffer, uint32_t buflen, uint32_t timeout);
int usbh_ch34x_bulk_out_transfer(struct usbh_ch34x *ch34x_class, uint8_t *buffer, uint32_t buflen, uint32_t timeout);
void usbh_ch34x_run(struct usbh_ch34x *ch34x_class);
void usbh_ch34x_stop(struct usbh_ch34x *ch34x_class);
#ifdef __cplusplus
}
#endif
#endif /* USBH_CH34X_H */

View File

@@ -1,328 +0,0 @@
/*
* Copyright (c) 2024, sakumisu
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "usbh_core.h"
#include "usbh_cp210x.h"
#define DEV_FORMAT "/dev/ttyUSB%d"
USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_cp210x_buf[USB_ALIGN_UP(64, CONFIG_USB_ALIGN_SIZE)];
#define CONFIG_USBHOST_MAX_CP210X_CLASS 1
static struct usbh_cp210x g_cp210x_class[CONFIG_USBHOST_MAX_CP210X_CLASS];
static uint32_t g_devinuse = 0;
static struct usbh_cp210x *usbh_cp210x_class_alloc(void)
{
uint8_t devno;
for (devno = 0; devno < CONFIG_USBHOST_MAX_CP210X_CLASS; devno++) {
if ((g_devinuse & (1U << devno)) == 0) {
g_devinuse |= (1U << devno);
memset(&g_cp210x_class[devno], 0, sizeof(struct usbh_cp210x));
g_cp210x_class[devno].minor = devno;
return &g_cp210x_class[devno];
}
}
return NULL;
}
static void usbh_cp210x_class_free(struct usbh_cp210x *cp210x_class)
{
uint8_t devno = cp210x_class->minor;
if (devno < 32) {
g_devinuse &= ~(1U << devno);
}
memset(cp210x_class, 0, sizeof(struct usbh_cp210x));
}
static int usbh_cp210x_enable(struct usbh_cp210x *cp210x_class)
{
struct usb_setup_packet *setup;
if (!cp210x_class || !cp210x_class->hport) {
return -USB_ERR_INVAL;
}
setup = cp210x_class->hport->setup;
setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_INTERFACE;
setup->bRequest = CP210X_IFC_ENABLE;
setup->wValue = 1;
setup->wIndex = cp210x_class->intf;
setup->wLength = 0;
return usbh_control_transfer(cp210x_class->hport, setup, NULL);
}
static int usbh_cp210x_set_flow(struct usbh_cp210x *cp210x_class)
{
struct usb_setup_packet *setup;
if (!cp210x_class || !cp210x_class->hport) {
return -USB_ERR_INVAL;
}
setup = cp210x_class->hport->setup;
setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_INTERFACE;
setup->bRequest = CP210X_SET_FLOW;
setup->wValue = 0;
setup->wIndex = cp210x_class->intf;
setup->wLength = 16;
memset(g_cp210x_buf, 0, 16);
g_cp210x_buf[13] = 0x20;
return usbh_control_transfer(cp210x_class->hport, setup, g_cp210x_buf);
}
static int usbh_cp210x_set_chars(struct usbh_cp210x *cp210x_class)
{
struct usb_setup_packet *setup;
if (!cp210x_class || !cp210x_class->hport) {
return -USB_ERR_INVAL;
}
setup = cp210x_class->hport->setup;
setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_INTERFACE;
setup->bRequest = CP210X_SET_CHARS;
setup->wValue = 0;
setup->wIndex = cp210x_class->intf;
setup->wLength = 6;
memset(g_cp210x_buf, 0, 6);
g_cp210x_buf[0] = 0x80;
g_cp210x_buf[4] = 0x88;
g_cp210x_buf[5] = 0x28;
return usbh_control_transfer(cp210x_class->hport, setup, g_cp210x_buf);
}
static int usbh_cp210x_set_baudrate(struct usbh_cp210x *cp210x_class, uint32_t baudrate)
{
struct usb_setup_packet *setup;
if (!cp210x_class || !cp210x_class->hport) {
return -USB_ERR_INVAL;
}
setup = cp210x_class->hport->setup;
setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_INTERFACE;
setup->bRequest = CP210X_SET_BAUDRATE;
setup->wValue = 0;
setup->wIndex = cp210x_class->intf;
setup->wLength = 4;
memcpy(g_cp210x_buf, (uint8_t *)&baudrate, 4);
return usbh_control_transfer(cp210x_class->hport, setup, g_cp210x_buf);
}
static int usbh_cp210x_set_data_format(struct usbh_cp210x *cp210x_class, uint8_t databits, uint8_t parity, uint8_t stopbits)
{
struct usb_setup_packet *setup;
uint16_t value;
if (!cp210x_class || !cp210x_class->hport) {
return -USB_ERR_INVAL;
}
setup = cp210x_class->hport->setup;
value = ((databits & 0x0F) << 8) | ((parity & 0x0f) << 4) | ((stopbits & 0x03) << 0);
setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_INTERFACE;
setup->bRequest = CP210X_SET_LINE_CTL;
setup->wValue = value;
setup->wIndex = cp210x_class->intf;
setup->wLength = 0;
return usbh_control_transfer(cp210x_class->hport, setup, NULL);
}
static int usbh_cp210x_set_mhs(struct usbh_cp210x *cp210x_class, uint8_t dtr, uint8_t rts, uint8_t dtr_mask, uint8_t rts_mask)
{
struct usb_setup_packet *setup;
uint16_t value;
if (!cp210x_class || !cp210x_class->hport) {
return -USB_ERR_INVAL;
}
setup = cp210x_class->hport->setup;
value = ((dtr & 0x01) << 0) | ((rts & 0x01) << 1) | ((dtr_mask & 0x01) << 8) | ((rts_mask & 0x01) << 9);
setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_INTERFACE;
setup->bRequest = CP210X_SET_MHS;
setup->wValue = value;
setup->wIndex = cp210x_class->intf;
setup->wLength = 0;
return usbh_control_transfer(cp210x_class->hport, setup, NULL);
}
int usbh_cp210x_set_line_coding(struct usbh_cp210x *cp210x_class, struct cdc_line_coding *line_coding)
{
memcpy((uint8_t *)&cp210x_class->line_coding, line_coding, sizeof(struct cdc_line_coding));
usbh_cp210x_set_baudrate(cp210x_class, line_coding->dwDTERate);
return usbh_cp210x_set_data_format(cp210x_class, line_coding->bDataBits, line_coding->bParityType, line_coding->bCharFormat);
}
int usbh_cp210x_get_line_coding(struct usbh_cp210x *cp210x_class, struct cdc_line_coding *line_coding)
{
memcpy(line_coding, (uint8_t *)&cp210x_class->line_coding, sizeof(struct cdc_line_coding));
return 0;
}
int usbh_cp210x_set_line_state(struct usbh_cp210x *cp210x_class, bool dtr, bool rts)
{
return usbh_cp210x_set_mhs(cp210x_class, dtr, rts, 1, 1);
}
static int usbh_cp210x_connect(struct usbh_hubport *hport, uint8_t intf)
{
struct usb_endpoint_descriptor *ep_desc;
int ret = 0;
struct usbh_cp210x *cp210x_class = usbh_cp210x_class_alloc();
if (cp210x_class == NULL) {
USB_LOG_ERR("Fail to alloc cp210x_class\r\n");
return -USB_ERR_NOMEM;
}
cp210x_class->hport = hport;
cp210x_class->intf = intf;
hport->config.intf[intf].priv = cp210x_class;
usbh_cp210x_enable(cp210x_class);
usbh_cp210x_set_flow(cp210x_class);
usbh_cp210x_set_chars(cp210x_class);
for (uint8_t i = 0; i < hport->config.intf[intf].altsetting[0].intf_desc.bNumEndpoints; i++) {
ep_desc = &hport->config.intf[intf].altsetting[0].ep[i].ep_desc;
if (ep_desc->bEndpointAddress & 0x80) {
USBH_EP_INIT(cp210x_class->bulkin, ep_desc);
} else {
USBH_EP_INIT(cp210x_class->bulkout, ep_desc);
}
}
snprintf(hport->config.intf[intf].devname, CONFIG_USBHOST_DEV_NAMELEN, DEV_FORMAT, cp210x_class->minor);
USB_LOG_INFO("Register CP210X Class:%s\r\n", hport->config.intf[intf].devname);
#if 0
USB_LOG_INFO("Test cp2102 rx and tx and rx for 5 times, baudrate is 115200\r\n");
struct cdc_line_coding linecoding;
uint8_t count = 5;
linecoding.dwDTERate = 115200;
linecoding.bDataBits = 8;
linecoding.bParityType = 0;
linecoding.bCharFormat = 0;
usbh_cp210x_set_line_coding(cp210x_class, &linecoding);
usbh_cp210x_set_line_state(cp210x_class, true, false);
memset(g_cp210x_buf, 'a', sizeof(g_cp210x_buf));
ret = usbh_cp210x_bulk_out_transfer(cp210x_class, g_cp210x_buf, sizeof(g_cp210x_buf), 0xfffffff);
USB_LOG_RAW("out ret:%d\r\n", ret);
while (count--) {
ret = usbh_cp210x_bulk_in_transfer(cp210x_class, g_cp210x_buf, sizeof(g_cp210x_buf), 0xfffffff);
USB_LOG_RAW("in ret:%d\r\n", ret);
if (ret > 0) {
for (uint32_t i = 0; i < ret; i++) {
USB_LOG_RAW("%02x ", g_cp210x_buf[i]);
}
USB_LOG_RAW("\r\n");
}
}
#endif
usbh_cp210x_run(cp210x_class);
return ret;
}
static int usbh_cp210x_disconnect(struct usbh_hubport *hport, uint8_t intf)
{
int ret = 0;
struct usbh_cp210x *cp210x_class = (struct usbh_cp210x *)hport->config.intf[intf].priv;
if (cp210x_class) {
if (cp210x_class->bulkin) {
usbh_kill_urb(&cp210x_class->bulkin_urb);
}
if (cp210x_class->bulkout) {
usbh_kill_urb(&cp210x_class->bulkout_urb);
}
if (hport->config.intf[intf].devname[0] != '\0') {
usb_osal_thread_schedule_other();
USB_LOG_INFO("Unregister CP210X Class:%s\r\n", hport->config.intf[intf].devname);
usbh_cp210x_stop(cp210x_class);
}
usbh_cp210x_class_free(cp210x_class);
}
return ret;
}
int usbh_cp210x_bulk_in_transfer(struct usbh_cp210x *cp210x_class, uint8_t *buffer, uint32_t buflen, uint32_t timeout)
{
int ret;
struct usbh_urb *urb = &cp210x_class->bulkin_urb;
usbh_bulk_urb_fill(urb, cp210x_class->hport, cp210x_class->bulkin, buffer, buflen, timeout, NULL, NULL);
ret = usbh_submit_urb(urb);
if (ret == 0) {
ret = urb->actual_length;
}
return ret;
}
int usbh_cp210x_bulk_out_transfer(struct usbh_cp210x *cp210x_class, uint8_t *buffer, uint32_t buflen, uint32_t timeout)
{
int ret;
struct usbh_urb *urb = &cp210x_class->bulkout_urb;
usbh_bulk_urb_fill(urb, cp210x_class->hport, cp210x_class->bulkout, buffer, buflen, timeout, NULL, NULL);
ret = usbh_submit_urb(urb);
if (ret == 0) {
ret = urb->actual_length;
}
return ret;
}
__WEAK void usbh_cp210x_run(struct usbh_cp210x *cp210x_class)
{
(void)cp210x_class;
}
__WEAK void usbh_cp210x_stop(struct usbh_cp210x *cp210x_class)
{
(void)cp210x_class;
}
static const uint16_t cp210x_id_table[][2] = {
{ 0x10C4, 0xEA60 },
{ 0, 0 },
};
const struct usbh_class_driver cp210x_class_driver = {
.driver_name = "cp210x",
.connect = usbh_cp210x_connect,
.disconnect = usbh_cp210x_disconnect
};
CLASS_INFO_DEFINE const struct usbh_class_info cp210x_class_info = {
.match_flags = USB_CLASS_MATCH_VID_PID | USB_CLASS_MATCH_INTF_CLASS,
.bInterfaceClass = 0xff,
.bInterfaceSubClass = 0x00,
.bInterfaceProtocol = 0x00,
.id_table = cp210x_id_table,
.class_driver = &cp210x_class_driver
};

View File

@@ -1,73 +0,0 @@
/*
* Copyright (c) 2024, sakumisu
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef USBH_CP210X_H
#define USBH_CP210X_H
#include "usb_cdc.h"
/* Requests */
#define CP210X_IFC_ENABLE 0x00
#define CP210X_SET_BAUDDIV 0x01
#define CP210X_GET_BAUDDIV 0x02
#define CP210X_SET_LINE_CTL 0x03 // Set parity, data bits, stop bits
#define CP210X_GET_LINE_CTL 0x04
#define CP210X_SET_BREAK 0x05
#define CP210X_IMM_CHAR 0x06
#define CP210X_SET_MHS 0x07 // Set DTR, RTS
#define CP210X_GET_MDMSTS 0x08
#define CP210X_SET_XON 0x09
#define CP210X_SET_XOFF 0x0A
#define CP210X_SET_EVENTMASK 0x0B
#define CP210X_GET_EVENTMASK 0x0C
#define CP210X_SET_CHAR 0x0D
#define CP210X_GET_CHARS 0x0E
#define CP210X_GET_PROPS 0x0F
#define CP210X_GET_COMM_STATUS 0x10
#define CP210X_RESET 0x11
#define CP210X_PURGE 0x12
#define CP210X_SET_FLOW 0x13
#define CP210X_GET_FLOW 0x14
#define CP210X_EMBED_EVENTS 0x15
#define CP210X_GET_EVENTSTATE 0x16
#define CP210X_SET_CHARS 0x19
#define CP210X_GET_BAUDRATE 0x1D
#define CP210X_SET_BAUDRATE 0x1E // Set baudrate
#define CP210X_VENDOR_SPECIFIC 0xFF
struct usbh_cp210x {
struct usbh_hubport *hport;
struct usb_endpoint_descriptor *bulkin; /* Bulk IN endpoint */
struct usb_endpoint_descriptor *bulkout; /* Bulk OUT endpoint */
struct usbh_urb bulkout_urb;
struct usbh_urb bulkin_urb;
struct cdc_line_coding line_coding;
uint8_t intf;
uint8_t minor;
void *user_data;
};
#ifdef __cplusplus
extern "C" {
#endif
int usbh_cp210x_set_line_coding(struct usbh_cp210x *ftdi_class, struct cdc_line_coding *line_coding);
int usbh_cp210x_get_line_coding(struct usbh_cp210x *ftdi_class, struct cdc_line_coding *line_coding);
int usbh_cp210x_set_line_state(struct usbh_cp210x *ftdi_class, bool dtr, bool rts);
int usbh_cp210x_bulk_in_transfer(struct usbh_cp210x *cp210x_class, uint8_t *buffer, uint32_t buflen, uint32_t timeout);
int usbh_cp210x_bulk_out_transfer(struct usbh_cp210x *cp210x_class, uint8_t *buffer, uint32_t buflen, uint32_t timeout);
void usbh_cp210x_run(struct usbh_cp210x *cp210x_class);
void usbh_cp210x_stop(struct usbh_cp210x *cp210x_class);
#ifdef __cplusplus
}
#endif
#endif /* USBH_CP210X_H */

File diff suppressed because it is too large Load Diff

View File

@@ -1,96 +0,0 @@
/*
* Copyright (c) 2024, sakumisu
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef USBH_FTDI_H
#define USBH_FTDI_H
#include "usb_cdc.h"
/* Requests */
#define SIO_RESET_REQUEST 0x00 /* Reset the port */
#define SIO_SET_MODEM_CTRL_REQUEST 0x01 /* Set the modem control register */
#define SIO_SET_FLOW_CTRL_REQUEST 0x02 /* Set flow control register */
#define SIO_SET_BAUDRATE_REQUEST 0x03 /* Set baud rate */
#define SIO_SET_DATA_REQUEST 0x04 /* Set the data characteristics of the port */
#define SIO_POLL_MODEM_STATUS_REQUEST 0x05
#define SIO_SET_EVENT_CHAR_REQUEST 0x06
#define SIO_SET_ERROR_CHAR_REQUEST 0x07
#define SIO_SET_LATENCY_TIMER_REQUEST 0x09
#define SIO_GET_LATENCY_TIMER_REQUEST 0x0A
#define SIO_SET_BITMODE_REQUEST 0x0B
#define SIO_READ_PINS_REQUEST 0x0C
#define SIO_READ_EEPROM_REQUEST 0x90
#define SIO_WRITE_EEPROM_REQUEST 0x91
#define SIO_ERASE_EEPROM_REQUEST 0x92
#define SIO_DISABLE_FLOW_CTRL 0x0
#define SIO_RTS_CTS_HS (0x1 << 8)
#define SIO_DTR_DSR_HS (0x2 << 8)
#define SIO_XON_XOFF_HS (0x4 << 8)
#define SIO_SET_DTR_MASK 0x1
#define SIO_SET_DTR_HIGH (1 | (SIO_SET_DTR_MASK << 8))
#define SIO_SET_DTR_LOW (0 | (SIO_SET_DTR_MASK << 8))
#define SIO_SET_RTS_MASK 0x2
#define SIO_SET_RTS_HIGH (2 | (SIO_SET_RTS_MASK << 8))
#define SIO_SET_RTS_LOW (0 | (SIO_SET_RTS_MASK << 8))
#define SIO_RTS_CTS_HS (0x1 << 8)
enum ftdi_chip_type {
SIO,
FT232A,
FT232B,
FT2232C,
FT232R,
FT232H,
FT2232H,
FT4232H,
FT4232HA,
FT232HP,
FT233HP,
FT2232HP,
FT2233HP,
FT4232HP,
FT4233HP,
FTX,
};
struct usbh_ftdi {
struct usbh_hubport *hport;
struct usb_endpoint_descriptor *bulkin; /* Bulk IN endpoint */
struct usb_endpoint_descriptor *bulkout; /* Bulk OUT endpoint */
struct usbh_urb bulkout_urb;
struct usbh_urb bulkin_urb;
struct cdc_line_coding line_coding;
uint8_t intf;
uint8_t minor;
uint8_t modem_status[2];
enum ftdi_chip_type chip_type;
void *user_data;
};
#ifdef __cplusplus
extern "C" {
#endif
int usbh_ftdi_set_line_coding(struct usbh_ftdi *ftdi_class, struct cdc_line_coding *line_coding);
int usbh_ftdi_get_line_coding(struct usbh_ftdi *ftdi_class, struct cdc_line_coding *line_coding);
int usbh_ftdi_set_line_state(struct usbh_ftdi *ftdi_class, bool dtr, bool rts);
int usbh_ftdi_bulk_in_transfer(struct usbh_ftdi *ftdi_class, uint8_t *buffer, uint32_t buflen, uint32_t timeout);
int usbh_ftdi_bulk_out_transfer(struct usbh_ftdi *ftdi_class, uint8_t *buffer, uint32_t buflen, uint32_t timeout);
void usbh_ftdi_run(struct usbh_ftdi *ftdi_class);
void usbh_ftdi_stop(struct usbh_ftdi *ftdi_class);
#ifdef __cplusplus
}
#endif
#endif /* USBH_FTDI_H */

View File

@@ -1,449 +0,0 @@
/*
* Copyright (c) 2024, sakumisu
* Copyright (c) 2024, Derek Konigsberg
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "usbh_core.h"
#include "usbh_pl2303.h"
#undef USB_DBG_TAG
#define USB_DBG_TAG "usbh_pl2303"
#include "usb_log.h"
#define DEV_FORMAT "/dev/ttyUSB%d"
USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_pl2303_buf[USB_ALIGN_UP(64, CONFIG_USB_ALIGN_SIZE)];
#define CONFIG_USBHOST_MAX_PL2303_CLASS 1
#define UT_WRITE_VENDOR_DEVICE (USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE)
#define UT_READ_VENDOR_DEVICE (USB_REQUEST_DIR_IN | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE)
static struct usbh_pl2303 g_pl2303_class[CONFIG_USBHOST_MAX_PL2303_CLASS];
static uint32_t g_devinuse = 0;
static struct usbh_pl2303 *usbh_pl2303_class_alloc(void)
{
uint8_t devno;
for (devno = 0; devno < CONFIG_USBHOST_MAX_PL2303_CLASS; devno++) {
if ((g_devinuse & (1U << devno)) == 0) {
g_devinuse |= (1U << devno);
memset(&g_pl2303_class[devno], 0, sizeof(struct usbh_pl2303));
g_pl2303_class[devno].minor = devno;
return &g_pl2303_class[devno];
}
}
return NULL;
}
static void usbh_pl2303_class_free(struct usbh_pl2303 *pl2303_class)
{
uint8_t devno = pl2303_class->minor;
if (devno < 32) {
g_devinuse &= ~(1U << devno);
}
memset(pl2303_class, 0, sizeof(struct usbh_pl2303));
}
static int usbh_pl2303_get_chiptype(struct usbh_pl2303 *pl2303_class)
{
int ret = 0;
switch (pl2303_class->hport->device_desc.bcdDevice) {
case 0x0300:
pl2303_class->chiptype = USBH_PL2303_TYPE_PL2303HX;
/* or TA, that is HX with external crystal */
break;
case 0x0400:
pl2303_class->chiptype = USBH_PL2303_TYPE_PL2303HXD;
/* or EA, that is HXD with ESD protection */
/* or RA, that has internal voltage level converter that works only up to 1Mbaud (!) */
break;
case 0x0500:
pl2303_class->chiptype = USBH_PL2303_TYPE_PL2303HXD;
/* in fact it's TB, that is HXD with external crystal */
break;
default:
/* NOTE: I have no info about the bcdDevice for the base PL2303 (up to 1.2Mbaud,
only fixed rates) and for PL2303SA (8-pin chip, up to 115200 baud */
/* Determine the chip type. This algorithm is taken from Linux. */
if (pl2303_class->hport->device_desc.bDeviceClass == 0x02) {
pl2303_class->chiptype = USBH_PL2303_TYPE_PL2303;
} else if (pl2303_class->hport->device_desc.bMaxPacketSize0 == 0x40) {
pl2303_class->chiptype = USBH_PL2303_TYPE_PL2303HX;
} else {
pl2303_class->chiptype = USBH_PL2303_TYPE_PL2303;
}
break;
}
/*
* The new chip revision PL2303HXN is only compatible with the new
* PLCOM_SET_REQUEST_PL2303HXN command. Issuing the old command
* PLCOM_SET_REQUEST to the new chip raises an error. Thus, PL2303HX
* and PL2303HXN can be distinguished by issuing an old-style request
* (on a status register) to the new chip and checking the error.
*/
if (pl2303_class->chiptype == USBH_PL2303_TYPE_PL2303HX) {
struct usb_setup_packet *setup = pl2303_class->hport->setup;
setup->bmRequestType = UT_READ_VENDOR_DEVICE;
setup->bRequest = PL2303_SET_REQUEST;
setup->wValue = PL2303_STATUS_REG_PL2303HX;
setup->wIndex = 0;
setup->wLength = 1;
ret = usbh_control_transfer(pl2303_class->hport, setup, g_pl2303_buf);
if (ret == -USB_ERR_STALL) {
pl2303_class->chiptype = USBH_PL2303_TYPE_PL2303HXN;
ret = 0;
} else if (ret < 0) {
USB_LOG_WRN("Error checking chip type: %d\r\n", ret);
return ret;
}
}
switch (pl2303_class->chiptype) {
case USBH_PL2303_TYPE_PL2303:
USB_LOG_INFO("chiptype = 2303\r\n");
break;
case USBH_PL2303_TYPE_PL2303HX:
USB_LOG_INFO("chiptype = 2303HX/TA\r\n");
break;
case USBH_PL2303_TYPE_PL2303HXN:
USB_LOG_INFO("chiptype = 2303HXN\r\n");
break;
case USBH_PL2303_TYPE_PL2303HXD:
USB_LOG_INFO("chiptype = 2303HXD/TB/RA/EA\r\n");
break;
default:
USB_LOG_INFO("chiptype = [%d]\r\n", pl2303_class->chiptype);
break;
}
return ret;
}
static int usbh_pl2303_do(struct usbh_pl2303 *pl2303_class,
uint8_t req_type, uint8_t request, uint16_t value, uint16_t index,
uint16_t length)
{
struct usb_setup_packet *setup;
if (!pl2303_class || !pl2303_class->hport) {
return -USB_ERR_INVAL;
}
setup = pl2303_class->hport->setup;
setup->bmRequestType = req_type;
setup->bRequest = request;
setup->wValue = value;
setup->wIndex = index;
setup->wLength = length;
return usbh_control_transfer(pl2303_class->hport, setup, g_pl2303_buf);
}
int usbh_pl2303_set_line_coding(struct usbh_pl2303 *pl2303_class, struct cdc_line_coding *line_coding)
{
struct usb_setup_packet *setup;
if (!pl2303_class || !pl2303_class->hport) {
return -USB_ERR_INVAL;
}
setup = pl2303_class->hport->setup;
setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE;
setup->bRequest = CDC_REQUEST_SET_LINE_CODING;
setup->wValue = 0;
setup->wIndex = pl2303_class->intf;
setup->wLength = 7;
memcpy(g_pl2303_buf, line_coding, sizeof(struct cdc_line_coding));
return usbh_control_transfer(pl2303_class->hport, setup, g_pl2303_buf);
}
int usbh_pl2303_get_line_coding(struct usbh_pl2303 *pl2303_class, struct cdc_line_coding *line_coding)
{
struct usb_setup_packet *setup;
int ret;
if (!pl2303_class || !pl2303_class->hport) {
return -USB_ERR_INVAL;
}
setup = pl2303_class->hport->setup;
setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE;
setup->bRequest = CDC_REQUEST_GET_LINE_CODING;
setup->wValue = 0;
setup->wIndex = pl2303_class->intf;
setup->wLength = 7;
ret = usbh_control_transfer(pl2303_class->hport, setup, g_pl2303_buf);
if (ret < 0) {
return ret;
}
memcpy(line_coding, g_pl2303_buf, sizeof(struct cdc_line_coding));
return ret;
}
int usbh_pl2303_set_line_state(struct usbh_pl2303 *pl2303_class, bool dtr, bool rts)
{
struct usb_setup_packet *setup;
if (!pl2303_class || !pl2303_class->hport) {
return -USB_ERR_INVAL;
}
setup = pl2303_class->hport->setup;
setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE;
setup->bRequest = CDC_REQUEST_SET_CONTROL_LINE_STATE;
setup->wValue = (dtr << 0) | (rts << 1);
setup->wIndex = pl2303_class->intf;
setup->wLength = 0;
return usbh_control_transfer(pl2303_class->hport, setup, NULL);
}
static int usbh_pl2303_connect(struct usbh_hubport *hport, uint8_t intf)
{
struct usb_endpoint_descriptor *ep_desc;
int ret = 0;
struct usbh_pl2303 *pl2303_class = usbh_pl2303_class_alloc();
if (pl2303_class == NULL) {
USB_LOG_ERR("Fail to alloc pl2303_class\r\n");
return -USB_ERR_NOMEM;
}
pl2303_class->hport = hport;
pl2303_class->intf = intf;
hport->config.intf[intf].priv = pl2303_class;
do {
ret = usbh_pl2303_get_chiptype(pl2303_class);
if (ret < 0) {
break;
}
/* Startup reset sequence, if necessary for the chip type */
if (pl2303_class->chiptype != USBH_PL2303_TYPE_PL2303HXN) {
struct usb_setup_packet *setup = pl2303_class->hport->setup;
setup->bmRequestType = UT_WRITE_VENDOR_DEVICE;
setup->bRequest = PL2303_SET_REQUEST;
setup->wValue = 0;
setup->wIndex = pl2303_class->intf;
setup->wLength = 0;
ret = usbh_control_transfer(pl2303_class->hport, setup, g_pl2303_buf);
if (ret < 0) {
USB_LOG_WRN("Initialization reset failed: %d\r\n", ret);
break;
}
}
if (pl2303_class->chiptype == USBH_PL2303_TYPE_PL2303) {
/* HX variants seem to lock up after a clear stall request. */
/*
* The FreeBSD code sets the stall flags on the in and out pipes
* here. Have no idea exactly how to do this, or if it is necessary.
* May just leave this code unwritten until test hardware is available.
*/
} else if (pl2303_class->chiptype == USBH_PL2303_TYPE_PL2303HX || pl2303_class->chiptype == USBH_PL2303_TYPE_PL2303HXD) {
/* Reset upstream data pipes */
ret = usbh_pl2303_do(pl2303_class, UT_WRITE_VENDOR_DEVICE, PL2303_SET_REQUEST, 8, 0, 0);
if (ret < 0) {
USB_LOG_WRN("Could not reset upstream data pipes (8,0): %d\r\n", ret);
break;
}
ret = usbh_pl2303_do(pl2303_class, UT_WRITE_VENDOR_DEVICE, PL2303_SET_REQUEST, 9, 0, 0);
if (ret < 0) {
USB_LOG_WRN("Could not reset upstream data pipes (9,0): %d\r\n", ret);
break;
}
} else if (pl2303_class->chiptype == USBH_PL2303_TYPE_PL2303HXN) {
/* Reset upstream data pipes */
ret = usbh_pl2303_do(pl2303_class, UT_WRITE_VENDOR_DEVICE, PL2303_SET_REQUEST_PL2303HXN, 0x07, 0x03, 0);
if (ret < 0) {
USB_LOG_WRN("Could not reset upstream data pipes (7,3): %d\r\n", ret);
break;
}
}
/* Final device initialization, if necessary for the chip type */
if (pl2303_class->chiptype != USBH_PL2303_TYPE_PL2303HXN) {
if (usbh_pl2303_do(pl2303_class, UT_READ_VENDOR_DEVICE, PL2303_SET_REQUEST, 0x8484, 0, 1) < 0 ||
usbh_pl2303_do(pl2303_class, UT_WRITE_VENDOR_DEVICE, PL2303_SET_REQUEST, 0x0404, 0, 0) < 0 ||
usbh_pl2303_do(pl2303_class, UT_READ_VENDOR_DEVICE, PL2303_SET_REQUEST, 0x8484, 0, 1) < 0 ||
usbh_pl2303_do(pl2303_class, UT_READ_VENDOR_DEVICE, PL2303_SET_REQUEST, 0x8383, 0, 1) < 0 ||
usbh_pl2303_do(pl2303_class, UT_READ_VENDOR_DEVICE, PL2303_SET_REQUEST, 0x8484, 0, 1) < 0 ||
usbh_pl2303_do(pl2303_class, UT_WRITE_VENDOR_DEVICE, PL2303_SET_REQUEST, 0x0404, 1, 0) < 0 ||
usbh_pl2303_do(pl2303_class, UT_READ_VENDOR_DEVICE, PL2303_SET_REQUEST, 0x8484, 0, 1) < 0 ||
usbh_pl2303_do(pl2303_class, UT_READ_VENDOR_DEVICE, PL2303_SET_REQUEST, 0x8383, 0, 1) < 0 ||
usbh_pl2303_do(pl2303_class, UT_WRITE_VENDOR_DEVICE, PL2303_SET_REQUEST, 0, 1, 0) < 0 ||
usbh_pl2303_do(pl2303_class, UT_WRITE_VENDOR_DEVICE, PL2303_SET_REQUEST, 1, 0, 0) < 0) {
USB_LOG_WRN("Could not complete init sequence\r\n");
ret = -USB_ERR_INVAL;
break;
}
if (pl2303_class->chiptype != USBH_PL2303_TYPE_PL2303) {
ret = usbh_pl2303_do(pl2303_class, UT_WRITE_VENDOR_DEVICE, PL2303_SET_REQUEST, 2, 0x44, 0);
} else {
ret = usbh_pl2303_do(pl2303_class, UT_WRITE_VENDOR_DEVICE, PL2303_SET_REQUEST, 2, 0x24, 0);
}
if (ret < 0) {
USB_LOG_WRN("Could not complete final init request: %d\r\n", ret);
break;
}
}
} while (0);
if (ret < 0) {
USB_LOG_ERR("Failed to initialize PL2303 device: %d\r\n", ret);
return ret;
}
for (uint8_t i = 0; i < hport->config.intf[intf].altsetting[0].intf_desc.bNumEndpoints; i++) {
ep_desc = &hport->config.intf[intf].altsetting[0].ep[i].ep_desc;
if (USB_GET_ENDPOINT_TYPE(ep_desc->bmAttributes) == USB_ENDPOINT_TYPE_INTERRUPT) {
continue;
} else {
if (ep_desc->bEndpointAddress & 0x80) {
USBH_EP_INIT(pl2303_class->bulkin, ep_desc);
} else {
USBH_EP_INIT(pl2303_class->bulkout, ep_desc);
}
}
}
snprintf(hport->config.intf[intf].devname, CONFIG_USBHOST_DEV_NAMELEN, DEV_FORMAT, pl2303_class->minor);
USB_LOG_INFO("Register PL2303 Class:%s\r\n", hport->config.intf[intf].devname);
#if 0
USB_LOG_INFO("Test pl2303 rx and tx and rx for 5 times, baudrate is 115200\r\n");
struct cdc_line_coding linecoding;
uint8_t count = 5;
linecoding.dwDTERate = 115200;
linecoding.bDataBits = 8;
linecoding.bParityType = 0;
linecoding.bCharFormat = 0;
usbh_pl2303_set_line_coding(pl2303_class, &linecoding);
usbh_pl2303_set_line_state(pl2303_class, true, false);
memset(g_pl2303_buf, 'a', sizeof(g_pl2303_buf));
ret = usbh_pl2303_bulk_out_transfer(pl2303_class, g_pl2303_buf, sizeof(g_pl2303_buf), 0xfffffff);
USB_LOG_RAW("out ret:%d\r\n", ret);
while (count--) {
ret = usbh_pl2303_bulk_in_transfer(pl2303_class, g_pl2303_buf, sizeof(g_pl2303_buf), 0xfffffff);
USB_LOG_RAW("in ret:%d\r\n", ret);
if (ret > 0) {
for (uint32_t i = 0; i < ret; i++) {
USB_LOG_RAW("%02x ", g_pl2303_buf[i]);
}
}
USB_LOG_RAW("\r\n");
}
#endif
usbh_pl2303_run(pl2303_class);
return ret;
}
static int usbh_pl2303_disconnect(struct usbh_hubport *hport, uint8_t intf)
{
int ret = 0;
struct usbh_pl2303 *pl2303_class = (struct usbh_pl2303 *)hport->config.intf[intf].priv;
if (pl2303_class) {
if (pl2303_class->bulkin) {
usbh_kill_urb(&pl2303_class->bulkin_urb);
}
if (pl2303_class->bulkout) {
usbh_kill_urb(&pl2303_class->bulkout_urb);
}
if (hport->config.intf[intf].devname[0] != '\0') {
usb_osal_thread_schedule_other();
USB_LOG_INFO("Unregister PL2303 Class:%s\r\n", hport->config.intf[intf].devname);
usbh_pl2303_stop(pl2303_class);
}
usbh_pl2303_class_free(pl2303_class);
}
return ret;
}
int usbh_pl2303_bulk_in_transfer(struct usbh_pl2303 *pl2303_class, uint8_t *buffer, uint32_t buflen, uint32_t timeout)
{
int ret;
struct usbh_urb *urb = &pl2303_class->bulkin_urb;
usbh_bulk_urb_fill(urb, pl2303_class->hport, pl2303_class->bulkin, buffer, buflen, timeout, NULL, NULL);
ret = usbh_submit_urb(urb);
if (ret == 0) {
ret = urb->actual_length;
}
return ret;
}
int usbh_pl2303_bulk_out_transfer(struct usbh_pl2303 *pl2303_class, uint8_t *buffer, uint32_t buflen, uint32_t timeout)
{
int ret;
struct usbh_urb *urb = &pl2303_class->bulkout_urb;
usbh_bulk_urb_fill(urb, pl2303_class->hport, pl2303_class->bulkout, buffer, buflen, timeout, NULL, NULL);
ret = usbh_submit_urb(urb);
if (ret == 0) {
ret = urb->actual_length;
}
return ret;
}
__WEAK void usbh_pl2303_run(struct usbh_pl2303 *pl2303_class)
{
(void)pl2303_class;
}
__WEAK void usbh_pl2303_stop(struct usbh_pl2303 *pl2303_class)
{
(void)pl2303_class;
}
static const uint16_t pl2303_id_table[][2] = {
{ 0x067B, 0x2303 }, // PL2303 Serial (ATEN/IOGEAR UC232A)
{ 0x067B, 0x23A3 }, // PL2303HXN Serial, type GC
{ 0x067B, 0x23B3 }, // PL2303HXN Serial, type GB
{ 0x067B, 0x23C3 }, // PL2303HXN Serial, type GT
{ 0x067B, 0x23D3 }, // PL2303HXN Serial, type GL
{ 0x067B, 0x23E3 }, // PL2303HXN Serial, type GE
{ 0x067B, 0x23F3 }, // PL2303HXN Serial, type GS
{ 0, 0 },
};
const struct usbh_class_driver pl2303_class_driver = {
.driver_name = "pl2303",
.connect = usbh_pl2303_connect,
.disconnect = usbh_pl2303_disconnect
};
CLASS_INFO_DEFINE const struct usbh_class_info pl2303_class_info = {
.match_flags = USB_CLASS_MATCH_VID_PID | USB_CLASS_MATCH_INTF_CLASS,
.bInterfaceClass = 0xff,
.bInterfaceSubClass = 0x00,
.bInterfaceProtocol = 0x00,
.id_table = pl2303_id_table,
.class_driver = &pl2303_class_driver
};

View File

@@ -1,62 +0,0 @@
/*
* Copyright (c) 2024, sakumisu
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef USBH_PL2303_H
#define USBH_PL2303_H
#include "usb_cdc.h"
#define PL2303_SET_REQUEST 0x01
#define PL2303_SET_REQUEST_PL2303HXN 0x80
#define PL2303_SET_CRTSCTS 0x41
#define PL2303_SET_CRTSCTS_PL2303X 0x61
#define PL2303_SET_CRTSCTS_PL2303HXN 0xFA
#define PL2303_CLEAR_CRTSCTS_PL2303HXN 0xFF
#define PL2303_CRTSCTS_REG_PL2303HXN 0x0A
#define PL2303_STATUS_REG_PL2303HX 0x8080
/* Different PL2303 IC types */
#define USBH_PL2303_TYPE_UNKNOWN 0
#define USBH_PL2303_TYPE_PL2303 1
#define USBH_PL2303_TYPE_PL2303HX 2
#define USBH_PL2303_TYPE_PL2303HXD 3
#define USBH_PL2303_TYPE_PL2303HXN 4
struct usbh_pl2303 {
struct usbh_hubport *hport;
struct usb_endpoint_descriptor *bulkin; /* Bulk IN endpoint */
struct usb_endpoint_descriptor *bulkout; /* Bulk OUT endpoint */
struct usbh_urb bulkout_urb;
struct usbh_urb bulkin_urb;
struct cdc_line_coding linecoding;
uint8_t intf;
uint8_t minor;
uint8_t chiptype;
void *user_data;
};
#ifdef __cplusplus
extern "C" {
#endif
int usbh_pl2303_set_line_coding(struct usbh_pl2303 *pl2303_class, struct cdc_line_coding *line_coding);
int usbh_pl2303_get_line_coding(struct usbh_pl2303 *pl2303_class, struct cdc_line_coding *line_coding);
int usbh_pl2303_set_line_state(struct usbh_pl2303 *pl2303_class, bool dtr, bool rts);
int usbh_pl2303_bulk_in_transfer(struct usbh_pl2303 *pl2303_class, uint8_t *buffer, uint32_t buflen, uint32_t timeout);
int usbh_pl2303_bulk_out_transfer(struct usbh_pl2303 *pl2303_class, uint8_t *buffer, uint32_t buflen, uint32_t timeout);
void usbh_pl2303_run(struct usbh_pl2303 *pl2303_class);
void usbh_pl2303_stop(struct usbh_pl2303 *pl2303_class);
#ifdef __cplusplus
}
#endif
#endif /* USBH_PL2303_H */

Some files were not shown because too many files have changed in this diff Show More