summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChen Leeren <[email protected]>2023-01-05 20:06:05 +0800
committersakumisu <[email protected]>2023-01-05 20:36:04 +0800
commit0be8327785de75029f1b27c899edfbbd1f62549f (patch)
tree07a83812150f11c5fc49c80151168c29ff998bc5
parent132db88885b827c004be79a2b29cf4eee495d1b6 (diff)
Add wch usb host support
-rw-r--r--port/ch32/usb_ch32_usbfs_reg.h65
-rw-r--r--port/ch32/usb_hc_usbfs.c1316
-rw-r--r--port/ch32/usb_hc_usbhs.c1341
3 files changed, 2722 insertions, 0 deletions
diff --git a/port/ch32/usb_ch32_usbfs_reg.h b/port/ch32/usb_ch32_usbfs_reg.h
index ca8e487a..9f3a38a3 100644
--- a/port/ch32/usb_ch32_usbfs_reg.h
+++ b/port/ch32/usb_ch32_usbfs_reg.h
@@ -98,6 +98,71 @@ typedef struct __attribute__((packed)) {
__IO uint32_t OTG_SR;
} USBOTGH_FS_TypeDef;
+typedef struct
+{
+ __IO uint8_t BASE_CTRL; /*!< 0x40008000 */
+ __IO uint8_t HOST_CTRL; /*!< 0x40008001 */
+ __IO uint8_t INT_EN; /*!< 0x40008002 */
+ __IO uint8_t DEV_ADDR; /*!< 0x40008003 */
+ __IO uint8_t USB_STATUS0; /*!< 0x40008004 */
+ __IO uint8_t MIS_ST; /*!< 0x40008005 */
+ __IO uint8_t INT_FG; /*!< 0x40008006 */
+ __IO uint8_t INT_ST; /*!< 0x40008007 */
+ __IO uint8_t RX_LEN; /*!< 0x40008008 */
+ __IO uint8_t Reserve1; /*!< 0x40008009 */
+ __IO uint8_t Reserve2; /*!< 0x4000800a */
+ __IO uint8_t Reserve3; /*!< 0x4000800b */
+ __IO uint8_t UEP4_1_MOD; /*!< 0x4000800c */
+ __IO uint8_t HOST_EP_MOD; /*!< 0x4000800d */
+ __IO uint8_t UEP567_MOD; /*!< 0x4000800e */
+ __IO uint8_t Reserve4; /*!< 0x4000800f */
+ __IO uint16_t UEP0_DMA; /*!< 0x40008010 */
+ __IO uint16_t Reserve5; /*!< 0x40008012 */
+ __IO uint16_t UEP1_DMA; /*!< 0x40008014 */
+ __IO uint16_t Reserve6; /*!< 0x40008016 */
+ __IO uint16_t HOST_RX_DMA; /*!< 0x40008018 */
+ __IO uint16_t Reserve7; /*!< 0x4000801a */
+ __IO uint16_t HOST_TX_DMA; /*!< 0x4000801c */
+ __IO uint16_t Reserve8; /*!< 0x4000801e */
+ __IO uint8_t UEP0_T_LEN; /*!< 0x40008020 */
+ __IO uint8_t Reserve9; /*!< 0x40008021 */
+ __IO uint8_t UEP0_CTRL; /*!< 0x40008022 */
+ __IO uint8_t Reserve10; /*!< 0x40008023 */
+ __IO uint8_t UEP1_T_LEN; /*!< 0x40008024 */
+ __IO uint8_t Reserve11; /*!< 0x40008025 */
+ __IO uint8_t HOST_SETUP; /*!< 0x40008026 */
+ __IO uint8_t Reserve12; /*!< 0x40008027 */
+ __IO uint8_t HOST_EP_PID; /*!< 0x40008028 */
+ __IO uint8_t Reserve13; /*!< 0x40008029 */
+ __IO uint8_t HOST_RX_CTRL; /*!< 0x4000802a */
+ __IO uint8_t Reserve14; /*!< 0x4000802b */
+ __IO uint8_t HOST_TX_LEN; /*!< 0x4000802c */
+ __IO uint8_t Reserve15; /*!< 0x4000802d */
+ __IO uint8_t HOST_TX_CTRL; /*!< 0x4000802e */
+ __IO uint8_t Reserve16; /*!< 0x4000802f */
+ __IO uint8_t UEP4_T_LEN; /*!< 0x40008030 */
+ __IO uint8_t Reserve17; /*!< 0x40008031 */
+ __IO uint8_t UEP4_CTRL; /*!< 0x40008032 */
+ __IO uint8_t Reserve18[33]; /*!< 0x40008033 */
+ __IO uint16_t UEP5_DMA; /*!< 0x40008054 */
+ __IO uint16_t Reserve19; /*!< 0x40008056 */
+ __IO uint16_t UEP6_DMA; /*!< 0x40008058 */
+ __IO uint16_t Reserve20; /*!< 0x4000805a */
+ __IO uint16_t UEP7_DMA; /*!< 0x4000805c */
+ __IO uint8_t Reserve21[6]; /*!< 0x4000805e */
+ __IO uint8_t UEP5_T_LEN; /*!< 0x40008064 */
+ __IO uint8_t Reserve22; /*!< 0x40008065 */
+ __IO uint8_t UEP5_CTRL; /*!< 0x40008066 */
+ __IO uint8_t Reserve23; /*!< 0x40008067 */
+ __IO uint8_t UEP6_T_LEN; /*!< 0x40008068 */
+ __IO uint8_t Reserve24; /*!< 0x40008069 */
+ __IO uint8_t UEP6_CTRL; /*!< 0x4000806a */
+ __IO uint8_t Reserve25; /*!< 0x4000806b */
+ __IO uint8_t UEP7_T_LEN; /*!< 0x4000806c */
+ __IO uint8_t Reserve26; /*!< 0x4000806d */
+ __IO uint8_t UEP7_CTRL; /*!< 0x4000806e */
+} USB_FS_TypeDef;
+
#define USBFS_BASE ((uint32_t)0x50000000)
#define USBFS_DEVICE ((USBOTG_FS_TypeDef *)USBFS_BASE)
diff --git a/port/ch32/usb_hc_usbfs.c b/port/ch32/usb_hc_usbfs.c
new file mode 100644
index 00000000..9e84eb95
--- /dev/null
+++ b/port/ch32/usb_hc_usbfs.c
@@ -0,0 +1,1316 @@
+#include "usbh_core.h"
+#include "usbh_hub.h"
+#include "usb_ch32_usbfs_reg.h"
+
+#if defined(CH581) || defined(CH582) || defined(CH583) || defined(CH571) || defined(CH572) || defined(CH573)
+#undef USBFS_BASE
+#undef USBFS_HOST
+
+#ifndef USBFS_BASE
+#pragma message "USB2 is used by default"
+#define USBFS_BASE ((uint32_t)0x40008400u)
+#endif
+
+#define USBFS_HOST ((USB_FS_TypeDef *)USBFS_BASE)
+
+#undef USBFS_UH_PRE_PID_EN
+#undef USBFS_UH_SOF_EN
+#define USBFS_UH_PRE_PID_EN 0x80 /*!< USB host PRE PID enable for low speed device via hub */
+#define USBFS_UH_SOF_EN 0x40 /*!< USB host automatic SOF enable */
+
+#undef USBFS_UH_R_AUTO_TOG
+#undef USBFS_UH_R_TOG
+#undef USBFS_UH_R_RES
+#define USBFS_UH_R_AUTO_TOG 0x10 /*!< enable automatic toggle after successful transfer completion: 0=manual toggle, 1=automatic toggle */
+#define USBFS_UH_R_TOG 0x80 /*!< expected data toggle flag of host receiving (IN): 0=DATA0, 1=DATA1 */
+#define USBFS_UH_R_RES 0x04 /*!< prepared handshake response type for host receiving (IN): 0=ACK (ready), 1=no response, time out to device, for isochronous transactions */
+
+#undef USBFS_UH_T_AUTO_TOG
+#undef USBFS_UH_T_TOG
+#undef USBFS_UH_T_RES
+#define USBFS_UH_T_AUTO_TOG 0x10 /*!< enable automatic toggle after successful transfer completion: 0=manual toggle, 1=automatic toggle */
+#define USBFS_UH_T_TOG 0x40 /*!< prepared data toggle flag of host transmittal (SETUP/OUT): 0=DATA0, 1=DATA1 */
+#define USBFS_UH_T_RES 0x01 /*!< expected handshake response type for host transmittal (SETUP/OUT): 0=ACK (ready), 1=no response, time out from device, for isochronous transactions */
+
+void USBH_IRQHandler(void) __attribute__((section(".highcode")));
+#elif defined(CH32F203) || defined(CH32F205) || defined(CH32F207) || defined(CH32F208)
+void USBH_IRQHandler(void) __attribute__((interrupt("WCH-Interrupt-fast")));
+#elif defined(CH32V203) || defined(CH32V303) || defined(CH32V305) || defined(CH32V307) || defined(CH32V208)
+void USBH_IRQHandler(void) __attribute__((interrupt("WCH-Interrupt-fast")));
+#else
+#error "Do not support"
+#endif
+
+#ifndef USBH_IRQHandler
+#pragma message "Please make sure your platform interrupt name"
+#define USBH_IRQHandler USB2_IRQHandler
+#endif
+
+#ifndef CONFIG_USBHOST_PIPE_NUM
+#define CONFIG_USBHOST_PIPE_NUM 8
+#endif
+
+typedef enum {
+ USB_EP0_STATE_SETUP = 0x0, /**< SETUP DATA */
+ USB_EP0_STATE_IN_DATA, /**< IN DATA */
+ USB_EP0_STATE_IN_STATUS, /**< IN status */
+ USB_EP0_STATE_OUT_DATA, /**< OUT DATA */
+ USB_EP0_STATE_OUT_STATUS, /**< OUT status */
+} ep0_state_t;
+
+struct chusb_pipe {
+ uint8_t dev_addr;
+ uint8_t ep_addr;
+ uint8_t ep_type;
+ uint8_t ep_interval;
+ uint8_t speed;
+ uint16_t ep_mps;
+ bool inuse;
+ uint32_t xfrd;
+ volatile bool waiter;
+
+ uint32_t xferlen;
+ uint8_t *buffer;
+ uint8_t data_pid;
+
+ usb_osal_sem_t waitsem;
+ struct usbh_hubport *hport;
+ struct usbh_urb *urb;
+};
+
+struct chusb_hcd {
+ volatile bool port_csc;
+ volatile bool port_pec;
+ volatile bool port_pe;
+ volatile uint8_t current_token;
+ volatile uint8_t ep0_state;
+ volatile uint8_t has_reseted;
+ volatile bool prv_get_zero;
+ volatile bool prv_set_zero;
+ volatile bool main_pipe_using;
+ uint32_t current_pipe_timeout;
+ struct chusb_pipe *current_pipe;
+ struct chusb_pipe pipe_pool[CONFIG_USBHOST_PIPE_NUM][2]; /* Support Bidirectional ep */
+} g_chusb_hcd;
+
+static inline void SET_UH_RX_CTRL_BIT(uint8_t bit)
+{
+ if ((USBFS_HOST->HOST_RX_CTRL & USBFS_UH_R_AUTO_TOG) != 0) {
+ /**
+ * USBFS_UH_R_TOG canot write
+ */
+ if ((bit & USBFS_UH_R_TOG) != 0) {
+ /**
+ * bit contains USBFS_UH_R_TOG
+ */
+ USBFS_HOST->HOST_RX_CTRL &= ~(USBFS_UH_R_AUTO_TOG);
+ USBFS_HOST->HOST_RX_CTRL |= (USBFS_UH_R_AUTO_TOG | bit);
+ } else {
+ USBFS_HOST->HOST_RX_CTRL |= bit;
+ }
+ } else {
+ USBFS_HOST->HOST_RX_CTRL |= bit;
+ }
+}
+
+static inline void SET_UH_TX_CTRL_BIT(uint8_t bit)
+{
+ if ((USBFS_HOST->HOST_TX_CTRL & USBFS_UH_T_AUTO_TOG) != 0) {
+ /**
+ * USBFS_UH_T_TOG canot write
+ */
+ if ((bit & USBFS_UH_T_TOG) != 0) {
+ /**
+ * bit contains USBFS_UH_T_TOG
+ */
+ USBFS_HOST->HOST_TX_CTRL &= ~(USBFS_UH_T_AUTO_TOG);
+ USBFS_HOST->HOST_TX_CTRL |= (USBFS_UH_T_AUTO_TOG | bit);
+ } else {
+ USBFS_HOST->HOST_TX_CTRL |= bit;
+ }
+ } else {
+ USBFS_HOST->HOST_TX_CTRL |= bit;
+ }
+}
+
+static inline void CLEAR_UH_TX_CTRL_BIT(uint8_t bit)
+{
+ if ((USBFS_HOST->HOST_TX_CTRL & USBFS_UH_T_AUTO_TOG) != 0) {
+ /**
+ * USBFS_UH_T_TOG canot write
+ */
+ if ((bit & USBFS_UH_T_TOG) != 0) {
+ /**
+ * bit contains USBFS_UH_T_TOG
+ */
+ USBFS_HOST->HOST_TX_CTRL &= ~(USBFS_UH_T_AUTO_TOG);
+ USBFS_HOST->HOST_TX_CTRL &= ~(bit);
+ USBFS_HOST->HOST_TX_CTRL |= USBFS_UH_T_AUTO_TOG;
+ } else {
+ USBFS_HOST->HOST_TX_CTRL &= ~(bit);
+ }
+ } else {
+ USBFS_HOST->HOST_TX_CTRL &= ~(bit);
+ }
+}
+
+static inline void CLEAR_UH_RX_CTRL_BIT(uint8_t bit)
+{
+ if ((USBFS_HOST->HOST_RX_CTRL & USBFS_UH_R_AUTO_TOG) != 0) {
+ /**
+ * USBFS_UH_R_TOG can't write
+ */
+ if ((bit & USBFS_UH_R_TOG) != 0) {
+ /**
+ * bit contains USBFS_UH_R_TOG
+ */
+ USBFS_HOST->HOST_RX_CTRL &= ~(USBFS_UH_R_AUTO_TOG);
+ USBFS_HOST->HOST_RX_CTRL &= ~(bit);
+ USBFS_HOST->HOST_RX_CTRL |= USBFS_UH_R_AUTO_TOG;
+ } else {
+ USBFS_HOST->HOST_RX_CTRL &= ~(bit);
+ }
+ } else {
+ USBFS_HOST->HOST_RX_CTRL &= ~(bit);
+ }
+}
+
+static inline void SET_UH_RX_CTRL(uint8_t value)
+{
+ USBFS_HOST->HOST_RX_CTRL = 0;
+ USBFS_HOST->HOST_RX_CTRL = value;
+ USB_LOG_DBG("USBFS_HOST->HOST_RX_CTRL:%02x\r\n", USBFS_HOST->HOST_RX_CTRL);
+}
+
+static inline void SET_UH_TX_CTRL(uint8_t value)
+{
+ USBFS_HOST->HOST_TX_CTRL = 0;
+ USBFS_HOST->HOST_TX_CTRL = value;
+ USB_LOG_DBG("USBFS_HOST->HOST_TX_CTRL:%02x\r\n", USBFS_HOST->HOST_TX_CTRL);
+}
+
+static inline void INT_PRE_HANDLER(void)
+{
+#if defined(CH581) || defined(CH582) || defined(CH583) || defined(CH571) || defined(CH572) || defined(CH573)
+#else
+ asm("csrrw sp,mscratch,sp");
+ extern void rt_interrupt_enter(void);
+ rt_interrupt_enter();
+#endif
+}
+
+static inline void INT_POST_HANDLER(void)
+{
+#if defined(CH581) || defined(CH582) || defined(CH583) || defined(CH571) || defined(CH572) || defined(CH573)
+#else
+ extern void rt_interrupt_leave(void);
+ rt_interrupt_leave();
+ asm("csrrw sp,mscratch,sp");
+#endif
+}
+
+static int8_t chusb_host_pipe_transfer(struct chusb_pipe *pipe, uint8_t pid, uint8_t *data, uint32_t len)
+{
+ /*!< Updata current transfer pid */
+ g_chusb_hcd.current_token = pid;
+ /*!< Updata curretn pipe */
+ g_chusb_hcd.current_pipe = pipe;
+ /*!< Updata curretn pipe timeout */
+ g_chusb_hcd.current_pipe_timeout = pipe->urb->timeout;
+ /*!< Updata main pipe using flag */
+ g_chusb_hcd.main_pipe_using = true;
+
+ if (data == NULL && len > 0) {
+ return -1;
+ }
+
+ if (pid == USB_PID_SETUP) {
+ /*!< Record the data len */
+ g_chusb_hcd.current_pipe->xferlen = len;
+
+ if ((uint32_t)data & 0x03) {
+ USB_LOG_INFO("SETUP DMA address is not align \r\n");
+ return -3;
+ }
+
+ USBFS_HOST->HOST_TX_DMA = (uint16_t)(uint32_t)data;
+
+ /*!< Record the data buffer address */
+ pipe->buffer = data;
+
+ if (len > pipe->ep_mps) {
+ len = pipe->ep_mps;
+ }
+
+ USBFS_HOST->HOST_TX_LEN = len;
+ USBFS_HOST->HOST_EP_PID = pid << 4 | (pipe->ep_addr & 0x0f);
+
+ } else if (pid == USB_PID_OUT) {
+ /*!< Record the data len */
+ g_chusb_hcd.current_pipe->xferlen = len;
+
+ if (len == 0) {
+ USBFS_HOST->HOST_TX_LEN = len;
+ USBFS_HOST->HOST_EP_PID = pid << 4 | (pipe->ep_addr & 0x0f);
+ return 0;
+ }
+
+ if ((uint32_t)data & 0x03) {
+ USB_LOG_INFO("OUT DMA address is not align \r\n");
+ return -3;
+ }
+
+ USBFS_HOST->HOST_TX_DMA = (uint16_t)(uint32_t)data;
+
+ /*!< Record the data buffer address */
+ pipe->buffer = data;
+
+ if (len > pipe->ep_mps) {
+ len = pipe->ep_mps;
+ }
+
+ USBFS_HOST->HOST_TX_LEN = len;
+ USBFS_HOST->HOST_EP_PID = pid << 4 | (pipe->ep_addr & 0x0f);
+
+ } else if (pid == USB_PID_IN) {
+ /*!< Record the data len */
+ g_chusb_hcd.current_pipe->xferlen = len;
+
+ if (len == 0) {
+ /*!< Want get 0 length data */
+ } else {
+ if ((uint32_t)data & 0x03) {
+ USB_LOG_INFO("IN DMA address is not align \r\n");
+ return -3;
+ }
+ USBFS_HOST->HOST_RX_DMA = (uint16_t)(uint32_t)data;
+ pipe->buffer = data;
+ }
+
+ USBFS_HOST->HOST_EP_PID = pid << 4 | (pipe->ep_addr & 0x0f);
+ }
+
+ return 0;
+}
+
+static void chusb_control_pipe_init(struct chusb_pipe *pipe, struct usb_setup_packet *setup, uint8_t *buffer, uint32_t buflen)
+{
+ if (g_chusb_hcd.ep0_state == USB_EP0_STATE_SETUP) {
+ /**
+ * Setup is distributed as DATA0
+ */
+ SET_UH_TX_CTRL(USBFS_UH_T_AUTO_TOG);
+ pipe->data_pid = 0;
+ chusb_host_pipe_transfer(pipe, USB_PID_SETUP, (uint8_t *)setup, 8);
+ } else if (g_chusb_hcd.ep0_state == USB_EP0_STATE_IN_DATA) {
+ if (pipe->data_pid != 1) {
+ USB_LOG_ERR("IN_DATA PID Error\r\n");
+ }
+ SET_UH_RX_CTRL(USBFS_UH_R_AUTO_TOG | USBFS_UH_R_TOG);
+ chusb_host_pipe_transfer(pipe, USB_PID_IN, buffer, buflen);
+ } else if (g_chusb_hcd.ep0_state == USB_EP0_STATE_OUT_DATA) {
+ if (pipe->data_pid != 1) {
+ USB_LOG_ERR("OUT_DATA PID Error\r\n");
+ }
+ chusb_host_pipe_transfer(pipe, USB_PID_OUT, buffer, buflen);
+ } else if (g_chusb_hcd.ep0_state == USB_EP0_STATE_IN_STATUS) {
+ /**
+ * Status stage must be DATA1
+ */
+ // SET_UH_RX_CTRL_BIT(USBFS_UH_R_TOG);
+ pipe->data_pid = 1;
+ SET_UH_RX_CTRL(USBFS_UH_R_AUTO_TOG | USBFS_UH_R_TOG);
+ chusb_host_pipe_transfer(pipe, USB_PID_IN, NULL, 0);
+ } else if (g_chusb_hcd.ep0_state == USB_EP0_STATE_OUT_STATUS) {
+ /**
+ * Status stage must be DATA1
+ */
+ // SET_UH_TX_CTRL_BIT(USBFS_UH_T_TOG);
+ pipe->data_pid = 1;
+ SET_UH_TX_CTRL(USBFS_UH_T_AUTO_TOG | USBFS_UH_T_TOG);
+ chusb_host_pipe_transfer(pipe, USB_PID_OUT, NULL, 0);
+ }
+}
+
+static void chusb_bulk_pipe_init(struct chusb_pipe *pipe, uint8_t *buffer, uint32_t buflen)
+{
+ if (pipe->ep_addr & 0x80) {
+ /*!< IN */
+ g_chusb_hcd.current_token = USB_PID_IN;
+ if (pipe->data_pid == 1) {
+ SET_UH_RX_CTRL(USBFS_UH_R_AUTO_TOG | USBFS_UH_R_TOG);
+ } else {
+ SET_UH_RX_CTRL(USBFS_UH_R_AUTO_TOG);
+ }
+ } else {
+ /*!< OUT */
+ g_chusb_hcd.current_token = USB_PID_OUT;
+ if (pipe->data_pid == 1) {
+ SET_UH_TX_CTRL(USBFS_UH_T_AUTO_TOG | USBFS_UH_T_TOG);
+ } else {
+ SET_UH_TX_CTRL(USBFS_UH_T_AUTO_TOG);
+ }
+ }
+
+ chusb_host_pipe_transfer(pipe, g_chusb_hcd.current_token, buffer, buflen);
+}
+
+static void chusb_intr_pipe_init(struct chusb_pipe *pipe, uint8_t *buffer, uint32_t buflen)
+{
+ if (pipe->ep_addr & 0x80) {
+ /*!< IN */
+ g_chusb_hcd.current_token = USB_PID_IN;
+ if (pipe->data_pid == 1) {
+ SET_UH_RX_CTRL(USBFS_UH_R_AUTO_TOG | USBFS_UH_R_TOG);
+ } else {
+ SET_UH_RX_CTRL(USBFS_UH_R_AUTO_TOG);
+ }
+ } else {
+ /*!< OUT */
+ g_chusb_hcd.current_token = USB_PID_OUT;
+ if (pipe->data_pid == 1) {
+ SET_UH_TX_CTRL(USBFS_UH_T_AUTO_TOG | USBFS_UH_T_TOG);
+ } else {
+ SET_UH_TX_CTRL(USBFS_UH_T_AUTO_TOG);
+ }
+ }
+
+ chusb_host_pipe_transfer(pipe, g_chusb_hcd.current_token, buffer, buflen);
+}
+
+static void chusb_iso_pipe_init(struct chusb_pipe *pipe, uint8_t *buffer, uint32_t buflen)
+{
+ if (pipe->ep_type != USB_ENDPOINT_TYPE_ISOCHRONOUS) {
+ USB_LOG_ERR("Endpoint type is not ISOCHRONOUS\r\n");
+ return;
+ }
+
+ if (pipe->ep_addr & 0x80) {
+ /*!< IN */
+ g_chusb_hcd.current_token = USB_PID_IN;
+ SET_UH_RX_CTRL(USBFS_UH_R_AUTO_TOG | USBFS_UH_R_RES);
+ } else {
+ /*!< OUT */
+ g_chusb_hcd.current_token = USB_PID_OUT;
+ SET_UH_TX_CTRL(USBFS_UH_T_AUTO_TOG | USBFS_UH_T_RES);
+ }
+
+ chusb_host_pipe_transfer(pipe, g_chusb_hcd.current_token, buffer, buflen);
+}
+
+static void chusbh_set_self_speed(uint8_t speed)
+{
+ if (speed == USB_SPEED_HIGH) {
+ } else if (speed == USB_SPEED_FULL) {
+ USBFS_HOST->BASE_CTRL &= ~USBFS_UC_LOW_SPEED;
+ USBFS_HOST->HOST_CTRL &= ~USBFS_UH_LOW_SPEED;
+ USBFS_HOST->HOST_SETUP &= ~USBFS_UH_PRE_PID_EN;
+ } else {
+ USBFS_HOST->BASE_CTRL |= USBFS_UC_LOW_SPEED;
+ USBFS_HOST->HOST_CTRL |= USBFS_UH_LOW_SPEED;
+ USBFS_HOST->HOST_SETUP |= USBFS_UH_PRE_PID_EN;
+ }
+}
+
+static int usbh_reset_port(const uint8_t port)
+{
+ g_chusb_hcd.has_reseted = 0x01;
+ g_chusb_hcd.port_pe = 0;
+ /*!< Set dev add 0 */
+ USBFS_HOST->DEV_ADDR = (USBFS_HOST->DEV_ADDR & USBFS_UDA_GP_BIT) | (0x00 & USBFS_USB_ADDR_MASK);
+ /*!< Close port */
+ USBFS_HOST->HOST_CTRL &= ~USBFS_UH_PORT_EN;
+ /*!< Start reset */
+ USBFS_HOST->HOST_CTRL |= USBFS_UH_BUS_RESET;
+ usb_osal_msleep(30);
+ /*!< Stop reset */
+ USBFS_HOST->HOST_CTRL &= ~USBFS_UH_BUS_RESET;
+ usb_osal_msleep(20);
+ /*!< Enable HUB Port */
+ USBFS_HOST->HOST_CTRL |= USBFS_UH_PORT_EN;
+ USBFS_HOST->HOST_SETUP |= USBFS_UH_SOF_EN;
+ g_chusb_hcd.port_pe = 1;
+ return 0;
+}
+
+static uint8_t usbh_get_port_speed(const uint8_t port)
+{
+ (void)port;
+ uint8_t speed = 0;
+
+ if (USBFS_HOST->MIS_ST & USBFS_UMS_DEV_ATTACH) {
+ speed = (USBFS_HOST->MIS_ST & USBFS_UMS_DM_LEVEL) ? 0 : 1;
+ if (speed == 0) {
+ /*!< Low speed */
+ USBFS_HOST->HOST_CTRL |= USBFS_UH_LOW_SPEED;
+ speed = USB_SPEED_LOW;
+ } else {
+ /*!< Full speed */
+ USBFS_HOST->HOST_CTRL &= ~USBFS_UH_LOW_SPEED;
+ speed = USB_SPEED_FULL;
+ }
+ }
+
+ return speed;
+}
+
+__WEAK void usb_hc_low_level_init(void)
+{
+}
+
+int usb_hc_init(void)
+{
+ memset(&g_chusb_hcd, 0, sizeof(struct chusb_hcd));
+
+ for (uint8_t i = 0; i < CONFIG_USBHOST_PIPE_NUM; i++) {
+ g_chusb_hcd.pipe_pool[i][0].waitsem = usb_osal_sem_create(0);
+ g_chusb_hcd.pipe_pool[i][1].waitsem = usb_osal_sem_create(0);
+ }
+
+ usb_hc_low_level_init();
+ USBFS_HOST->BASE_CTRL = USBFS_UC_RESET_SIE | USBFS_UC_CLR_ALL;
+ static uint32_t wait_ct = 50000;
+ while (wait_ct--) {
+ }
+ USBFS_HOST->BASE_CTRL = 0;
+
+ USBFS_HOST->BASE_CTRL = USBFS_UC_HOST_MODE;
+ USBFS_HOST->HOST_CTRL = 0;
+
+ USBFS_HOST->DEV_ADDR = 0x00;
+
+ USBFS_HOST->HOST_EP_MOD = USBFS_UH_EP_TX_EN | USBFS_UH_EP_RX_EN;
+
+ USBFS_HOST->HOST_RX_CTRL = 0;
+ USBFS_HOST->HOST_TX_CTRL = 0;
+
+ USBFS_HOST->BASE_CTRL = USBFS_UC_HOST_MODE | USBFS_UC_INT_BUSY | USBFS_UC_DMA_EN;
+ // USBFS_HOST->HOST_SETUP = USBFS_UH_SOF_EN;
+ USBFS_HOST->INT_FG = 0xFF;
+
+ USBFS_HOST->INT_EN = USBFS_UIE_TRANSFER | USBFS_UIE_DETECT;
+ return 0;
+}
+
+int usbh_roothub_control(struct usb_setup_packet *setup, uint8_t *buf)
+{
+ uint8_t nports;
+ uint8_t port;
+ uint32_t status;
+
+ nports = CONFIG_USBHOST_MAX_RHPORTS;
+ port = setup->wIndex;
+ if (setup->bmRequestType & USB_REQUEST_RECIPIENT_DEVICE) {
+ switch (setup->bRequest) {
+ case HUB_REQUEST_CLEAR_FEATURE:
+ switch (setup->wValue) {
+ case HUB_FEATURE_HUB_C_LOCALPOWER:
+ break;
+ case HUB_FEATURE_HUB_C_OVERCURRENT:
+ break;
+ default:
+ return -EPIPE;
+ }
+ break;
+ case HUB_REQUEST_SET_FEATURE:
+ switch (setup->wValue) {
+ case HUB_FEATURE_HUB_C_LOCALPOWER:
+ break;
+ case HUB_FEATURE_HUB_C_OVERCURRENT:
+ break;
+ default:
+ return -EPIPE;
+ }
+ break;
+ case HUB_REQUEST_GET_DESCRIPTOR:
+ break;
+ case HUB_REQUEST_GET_STATUS:
+ memset(buf, 0, 4);
+ break;
+ default:
+ break;
+ }
+ } else if (setup->bmRequestType & USB_REQUEST_RECIPIENT_OTHER) {
+ switch (setup->bRequest) {
+ case HUB_REQUEST_CLEAR_FEATURE:
+ if (!port || port > nports) {
+ return -EPIPE;
+ }
+
+ switch (setup->wValue) {
+ case HUB_PORT_FEATURE_ENABLE:
+ break;
+ case HUB_PORT_FEATURE_SUSPEND:
+ case HUB_PORT_FEATURE_C_SUSPEND:
+ break;
+ case HUB_PORT_FEATURE_POWER:
+ break;
+ case HUB_PORT_FEATURE_C_CONNECTION:
+ g_chusb_hcd.port_csc = 0;
+ break;
+ case HUB_PORT_FEATURE_C_ENABLE:
+ g_chusb_hcd.port_pec = 0;
+ break;
+ case HUB_PORT_FEATURE_C_OVER_CURREN:
+ break;
+ case HUB_PORT_FEATURE_C_RESET:
+ break;
+ default:
+ return -EPIPE;
+ }
+ break;
+ case HUB_REQUEST_SET_FEATURE:
+ if (!port || port > nports) {
+ return -EPIPE;
+ }
+
+ switch (setup->wValue) {
+ case HUB_PORT_FEATURE_SUSPEND:
+ break;
+ case HUB_PORT_FEATURE_POWER:
+ break;
+ case HUB_PORT_FEATURE_RESET:
+ usbh_reset_port(port);
+ break;
+
+ default:
+ return -EPIPE;
+ }
+ break;
+ case HUB_REQUEST_GET_STATUS:
+ if (!port || port > nports) {
+ return -EPIPE;
+ }
+
+ status = 0;
+ if (g_chusb_hcd.port_csc) {
+ status |= (1 << HUB_PORT_FEATURE_C_CONNECTION);
+ }
+ if (g_chusb_hcd.port_pec) {
+ status |= (1 << HUB_PORT_FEATURE_C_ENABLE);
+ }
+
+ if (g_chusb_hcd.port_pe) {
+ status |= (1 << HUB_PORT_FEATURE_CONNECTION);
+ status |= (1 << HUB_PORT_FEATURE_ENABLE);
+ if (usbh_get_port_speed(port) == USB_SPEED_LOW) {
+ status |= (1 << HUB_PORT_FEATURE_LOWSPEED);
+ } else if (usbh_get_port_speed(port) == USB_SPEED_HIGH) {
+ status |= (1 << HUB_PORT_FEATURE_HIGHSPEED);
+ }
+ }
+
+ memcpy(buf, &status, 4);
+ break;
+ default:
+ break;
+ }
+ }
+ return 0;
+}
+
+int usbh_ep0_pipe_reconfigure(usbh_pipe_t pipe, uint8_t dev_addr, uint8_t ep_mps, uint8_t speed)
+{
+ struct chusb_pipe *ppipe = (struct chusb_pipe *)pipe;
+
+ ppipe->dev_addr = dev_addr;
+ ppipe->ep_mps = ep_mps;
+ ppipe->speed = speed;
+
+ if (speed == USB_SPEED_HIGH) {
+ USB_LOG_INFO("ep0 reconfigure USB_SPEED_HIGH \r\n");
+ } else if (speed == USB_SPEED_FULL) {
+ USB_LOG_INFO("ep0 reconfigure USB_SPEED_FULL \r\n");
+ chusbh_set_self_speed(USB_SPEED_FULL);
+ } else if (speed == USB_SPEED_LOW) {
+ USB_LOG_INFO("ep0 reconfigure USB_SPEED_LOW \r\n");
+ chusbh_set_self_speed(USB_SPEED_LOW);
+ }
+
+ USBFS_HOST->DEV_ADDR = (USBFS_DEV_ADDR_OFFSET & USBFS_UDA_GP_BIT) | (dev_addr & USBFS_USB_ADDR_MASK);
+ return 0;
+}
+
+int usbh_pipe_alloc(usbh_pipe_t *pipe, const struct usbh_endpoint_cfg *ep_cfg)
+{
+ struct chusb_pipe *ppipe;
+ uint8_t ep_idx;
+ usb_osal_sem_t waitsem;
+
+ ep_idx = ep_cfg->ep_addr & 0x7f;
+
+ if (ep_idx > CONFIG_USBHOST_PIPE_NUM) {
+ return -ENOMEM;
+ }
+
+ if (ep_cfg->ep_addr & 0x80) {
+ ppipe = &g_chusb_hcd.pipe_pool[ep_idx][1];
+ } else {
+ ppipe = &g_chusb_hcd.pipe_pool[ep_idx][0];
+ }
+
+ /* store variables */
+ waitsem = ppipe->waitsem;
+
+ memset(ppipe, 0, sizeof(struct chusb_pipe));
+
+ ppipe->ep_addr = ep_cfg->ep_addr;
+ ppipe->ep_type = ep_cfg->ep_type;
+ ppipe->ep_mps = ep_cfg->ep_mps;
+ ppipe->ep_interval = ep_cfg->ep_interval;
+ ppipe->speed = ep_cfg->hport->speed;
+ ppipe->dev_addr = ep_cfg->hport->dev_addr;
+ ppipe->hport = ep_cfg->hport;
+
+ if (ep_cfg->ep_type == USB_ENDPOINT_TYPE_CONTROL) {
+ } else {
+ if (ppipe->speed == USB_SPEED_HIGH) {
+ } else if (ppipe->speed == USB_SPEED_FULL) {
+ } else if (ppipe->speed == USB_SPEED_LOW) {
+ }
+ }
+ /* restore variable */
+ ppipe->inuse = true;
+ ppipe->waitsem = waitsem;
+
+ *pipe = (usbh_pipe_t)ppipe;
+ return 0;
+}
+
+int usbh_pipe_free(usbh_pipe_t pipe)
+{
+ return 0;
+}
+
+int usbh_submit_urb(struct usbh_urb *urb)
+{
+ struct chusb_pipe *pipe;
+ size_t flags;
+ int ret = 0;
+
+ if (!urb) {
+ USB_LOG_INFO("urb is null \r\n");
+ return -EINVAL;
+ }
+
+ pipe = urb->pipe;
+
+ if (!pipe) {
+ USB_LOG_INFO("pipe is null \r\n");
+ return -EINVAL;
+ }
+
+ if (!pipe->hport->connected) {
+ USB_LOG_INFO("!pipe->hport->connected \r\n");
+ return -ENODEV;
+ }
+
+ if (pipe->urb) {
+ USB_LOG_INFO("pipe->urb is not null\r\n");
+ return -EBUSY;
+ }
+
+ if (g_chusb_hcd.main_pipe_using == true) {
+ USB_LOG_INFO("usbh_submit_urb//main pipe is using\r\n");
+ return -EBUSY;
+ }
+
+ flags = usb_osal_enter_critical_section();
+
+ pipe->waiter = false;
+ pipe->xfrd = 0;
+ pipe->urb = urb;
+ urb->errorcode = -EBUSY;
+ urb->actual_length = 0;
+
+ if (urb->timeout > 0) {
+ pipe->waiter = true;
+ }
+ usb_osal_leave_critical_section(flags);
+
+ switch (pipe->ep_type) {
+ case USB_ENDPOINT_TYPE_CONTROL:
+ g_chusb_hcd.ep0_state = USB_EP0_STATE_SETUP;
+ chusb_control_pipe_init(pipe, urb->setup, urb->transfer_buffer, urb->transfer_buffer_length);
+ break;
+ case USB_ENDPOINT_TYPE_BULK:
+ chusb_bulk_pipe_init(pipe, urb->transfer_buffer, urb->transfer_buffer_length);
+ break;
+ case USB_ENDPOINT_TYPE_INTERRUPT:
+ chusb_intr_pipe_init(pipe, urb->transfer_buffer, urb->transfer_buffer_length);
+ break;
+ case USB_ENDPOINT_TYPE_ISOCHRONOUS:
+ chusb_iso_pipe_init(pipe, urb->transfer_buffer, urb->transfer_buffer_length);
+ break;
+ default:
+ break;
+ }
+ if (urb->timeout > 0) {
+ /* wait until timeout or sem give */
+ ret = usb_osal_sem_take(pipe->waitsem, urb->timeout);
+ if (ret < 0) {
+ goto errout_timeout;
+ }
+
+ g_chusb_hcd.current_pipe_timeout = 0;
+
+ ret = urb->errorcode;
+ }
+ return ret;
+errout_timeout:
+ pipe->waiter = false;
+ usbh_kill_urb(urb);
+ return ret;
+}
+
+int usbh_kill_urb(struct usbh_urb *urb)
+{
+ return 0;
+}
+
+static inline void chusb_pipe_waitup(struct chusb_pipe *pipe, bool callback)
+{
+ struct usbh_urb *urb;
+
+ urb = pipe->urb;
+ pipe->urb = NULL;
+ g_chusb_hcd.main_pipe_using = false;
+
+ if (pipe->waiter) {
+ pipe->waiter = false;
+ usb_osal_sem_give(pipe->waitsem);
+ }
+
+ if (callback == true) {
+ if (urb->complete) {
+ if (urb->errorcode < 0) {
+ urb->complete(urb->arg, urb->errorcode);
+ } else {
+ urb->complete(urb->arg, urb->actual_length);
+ }
+ }
+ }
+}
+
+static int8_t chusb_outpipe_irq_handler(uint8_t res_state)
+{
+ uint16_t current_tx_length = USBFS_HOST->HOST_TX_LEN;
+ struct usbh_urb *urb;
+ urb = (g_chusb_hcd.current_pipe->urb);
+
+ if (g_chusb_hcd.current_pipe->ep_type != USB_ENDPOINT_TYPE_CONTROL) {
+ if ((g_chusb_hcd.current_pipe->ep_addr & 0x80) != 0) {
+ /* Error */
+ USB_LOG_ERR("ep_addr is not out add \r\n");
+ USBFS_HOST->INT_FG = USBFS_UIF_TRANSFER;
+ return -1;
+ }
+ }
+
+ switch (res_state) {
+ case USB_PID_STALL:
+ urb->errorcode = -EPERM;
+ if (g_chusb_hcd.current_token == USB_PID_SETUP) {
+ USB_LOG_ERR("USB_PID_SETUP STALL \r\n");
+ } else {
+ USB_LOG_ERR("USB_PID_OUT STALL \r\n");
+ if (g_chusb_hcd.current_pipe->ep_type != USB_ENDPOINT_TYPE_CONTROL) {
+ /**
+ * Reset data pid
+ */
+ g_chusb_hcd.current_pipe->data_pid = 0;
+ }
+ }
+ USBFS_HOST->INT_FG = USBFS_UIF_TRANSFER;
+ return -2;
+ case USB_PID_NAK:
+ urb->errorcode = -EAGAIN;
+ if (g_chusb_hcd.current_pipe->ep_type == USB_ENDPOINT_TYPE_CONTROL) {
+ if (g_chusb_hcd.current_token == USB_PID_SETUP) {
+ /**
+ * Device must ack for setup package
+ */
+ USB_LOG_ERR("Setup NAK \r\n");
+ g_chusb_hcd.current_token = 0;
+ USBFS_HOST->INT_FG = USBFS_UIF_TRANSFER;
+ return -3;
+ } else {
+ if (g_chusb_hcd.current_pipe_timeout > 0) {
+ USB_LOG_DBG("Control endpoint out nak and retry\r\n");
+ chusb_host_pipe_transfer(g_chusb_hcd.current_pipe, USB_PID_OUT,
+ g_chusb_hcd.current_pipe->buffer, g_chusb_hcd.current_pipe->xferlen);
+ }
+ }
+ } else {
+ if (g_chusb_hcd.prv_set_zero == true) {
+ /**
+ * It is unlikely to run here,
+ * because the device can probably receive 0 length byte packets
+ */
+ urb->errorcode = 0;
+ g_chusb_hcd.prv_set_zero = false;
+ g_chusb_hcd.current_token = 0;
+ urb->actual_length = g_chusb_hcd.current_pipe->xfrd;
+ chusb_pipe_waitup(g_chusb_hcd.current_pipe, true);
+ } else {
+ if (g_chusb_hcd.current_pipe_timeout > 0) {
+ USB_LOG_DBG("Normal endpoint out nak and retry\r\n");
+ urb->errorcode = -EAGAIN;
+ chusb_host_pipe_transfer(g_chusb_hcd.current_pipe, USB_PID_OUT,
+ g_chusb_hcd.current_pipe->buffer, g_chusb_hcd.current_pipe->xferlen);
+ } else {
+ g_chusb_hcd.current_token = 0;
+ if (g_chusb_hcd.current_pipe->xfrd > 0) {
+ /**
+ * The device received data but did not receive it completely
+ */
+ urb->errorcode = -EBUSY;
+ USB_LOG_WRN("The data is not sent completely, but the timeout is 0\r\n");
+ urb->actual_length = g_chusb_hcd.current_pipe->xfrd;
+ chusb_pipe_waitup(g_chusb_hcd.current_pipe, true);
+ } else {
+ /**
+ * The device may not be ready, make sure your device can receive data
+ */
+ USB_LOG_WRN("Your device seems unable to receive data\r\n");
+ urb->errorcode = -EBUSY;
+ chusb_pipe_waitup(g_chusb_hcd.current_pipe, false);
+ }
+ }
+ }
+ }
+ break;
+ case USB_PID_ACK:
+ /**
+ * The last out or setup package was sent
+ */
+ urb->errorcode = 0;
+
+ if (g_chusb_hcd.current_pipe->ep_type == USB_ENDPOINT_TYPE_CONTROL) {
+ /*!< Ctrol endpoint */
+ if (g_chusb_hcd.current_token == USB_PID_SETUP) {
+ /*!< Setup package send successfully */
+ urb->actual_length += 8;
+ g_chusb_hcd.current_pipe->data_pid ^= 1;
+ if (g_chusb_hcd.ep0_state == USB_EP0_STATE_SETUP) {
+ if (urb->setup->wLength) {
+ if (urb->setup->bmRequestType & 0x80) {
+ g_chusb_hcd.ep0_state = USB_EP0_STATE_IN_DATA;
+ } else {
+ g_chusb_hcd.ep0_state = USB_EP0_STATE_OUT_DATA;
+ }
+ } else {
+ g_chusb_hcd.ep0_state = USB_EP0_STATE_IN_STATUS;
+ }
+ chusb_control_pipe_init(g_chusb_hcd.current_pipe, urb->setup,
+ urb->transfer_buffer, urb->transfer_buffer_length);
+ }
+ } else if (g_chusb_hcd.current_token == USB_PID_OUT) {
+ if (g_chusb_hcd.ep0_state == USB_EP0_STATE_OUT_DATA) {
+ USB_LOG_DBG("ep0 tx_len:%d\r\n", current_tx_length);
+ g_chusb_hcd.current_pipe->data_pid ^= 1;
+ g_chusb_hcd.current_pipe->xfrd += current_tx_length;
+ g_chusb_hcd.current_pipe->buffer += current_tx_length;
+ g_chusb_hcd.current_pipe->xferlen -= current_tx_length;
+ if (g_chusb_hcd.current_pipe->xferlen == 0) {
+ if (current_tx_length == g_chusb_hcd.current_pipe->ep_mps) {
+ /**
+ * Need send 0 length data
+ */
+ chusb_host_pipe_transfer(g_chusb_hcd.current_pipe, USB_PID_OUT, NULL, 0);
+ } else {
+ /*!< Out package send successfully */
+ g_chusb_hcd.ep0_state = USB_EP0_STATE_IN_STATUS;
+ chusb_control_pipe_init(g_chusb_hcd.current_pipe, urb->setup,
+ urb->transfer_buffer, urb->transfer_buffer_length);
+ }
+ } else {
+ /*!< Start send next out package */
+ chusb_host_pipe_transfer(g_chusb_hcd.current_pipe, USB_PID_OUT,
+ g_chusb_hcd.current_pipe->buffer, g_chusb_hcd.current_pipe->xferlen);
+ }
+ } else if (g_chusb_hcd.ep0_state == USB_EP0_STATE_OUT_STATUS) {
+ g_chusb_hcd.ep0_state = USB_EP0_STATE_SETUP;
+ urb->actual_length += g_chusb_hcd.current_pipe->xfrd;
+ USB_LOG_DBG("S-> I-> O-status stage: In:%d\r\n", urb->actual_length - 8);
+ if (g_chusb_hcd.current_pipe->data_pid != 1) {
+ USB_LOG_ERR("S-> I-> O-status stage DATA PID Error\r\n");
+ } else {
+ g_chusb_hcd.current_pipe->data_pid ^= 1;
+ }
+ chusb_pipe_waitup(g_chusb_hcd.current_pipe, true);
+ }
+ }
+ } else {
+ if (g_chusb_hcd.current_token == USB_PID_OUT) {
+ USB_LOG_DBG("ep%d tx_len:%d\r\n", (g_chusb_hcd.current_pipe->ep_addr & 0x0f), current_tx_length);
+ g_chusb_hcd.current_pipe->data_pid ^= 1;
+ g_chusb_hcd.current_pipe->xfrd += current_tx_length;
+ g_chusb_hcd.current_pipe->buffer += current_tx_length;
+ g_chusb_hcd.current_pipe->xferlen -= current_tx_length;
+ if (g_chusb_hcd.current_pipe->xferlen == 0) {
+ if (current_tx_length == g_chusb_hcd.current_pipe->ep_mps) {
+ /**
+ * Need send 0 length data
+ */
+ if (g_chusb_hcd.prv_set_zero == true) {
+ USB_LOG_ERR("g_chusb_hcd.prv_set_zero is always true\r\n");
+ }
+ g_chusb_hcd.prv_set_zero = true;
+ chusb_host_pipe_transfer(g_chusb_hcd.current_pipe, USB_PID_OUT, NULL, 0);
+ } else {
+ /*!< Out package send successfully */
+ g_chusb_hcd.current_token = 0;
+ if (g_chusb_hcd.prv_set_zero == true) {
+ g_chusb_hcd.prv_set_zero = false;
+ }
+ urb->actual_length = g_chusb_hcd.current_pipe->xfrd;
+ chusb_pipe_waitup(g_chusb_hcd.current_pipe, true);
+ }
+ } else {
+ /*!< Start send next out package */
+ chusb_host_pipe_transfer(g_chusb_hcd.current_pipe, USB_PID_OUT,
+ g_chusb_hcd.current_pipe->buffer, g_chusb_hcd.current_pipe->xferlen);
+ }
+ } else {
+ USB_LOG_ERR("Error token is not match \r\n");
+ USBFS_HOST->INT_FG = USBFS_UIF_TRANSFER;
+ return -4;
+ }
+ }
+ break;
+ case 0:
+ if (g_chusb_hcd.current_pipe->ep_type != USB_ENDPOINT_TYPE_ISOCHRONOUS) {
+ if (g_chusb_hcd.current_token == USB_PID_SETUP) {
+ USB_LOG_ERR("SETUP TIMEOUT \r\n");
+ } else {
+ USB_LOG_ERR("OUT TIMEOUT \r\n");
+ if (g_chusb_hcd.current_pipe->ep_type != USB_ENDPOINT_TYPE_CONTROL) {
+ /**
+ * Reset data pid
+ */
+ g_chusb_hcd.current_pipe->data_pid = 0;
+ }
+ }
+ urb->errorcode = -EIO;
+ USBFS_HOST->INT_FG = USBFS_UIF_TRANSFER;
+ return -5;
+ } else {
+ /**
+ * No response from isochronous endpoint out
+ */
+ urb->errorcode = 0;
+ chusb_pipe_waitup(g_chusb_hcd.current_pipe, true);
+ }
+ default:
+ break;
+ }
+
+ USBFS_HOST->INT_FG = USBFS_UIF_TRANSFER;
+ return 0;
+}
+
+static int8_t chusb_inpipe_irq_handler(uint8_t res_state)
+{
+ uint16_t rx_len = 0;
+ struct usbh_urb *urb;
+ urb = (g_chusb_hcd.current_pipe->urb);
+
+ if (g_chusb_hcd.current_pipe->ep_type != USB_ENDPOINT_TYPE_CONTROL) {
+ if ((g_chusb_hcd.current_pipe->ep_addr & 0x80) == 0) {
+ /* Error */
+ USB_LOG_ERR("ep_addr is not in add\r\n");
+ USBFS_HOST->INT_FG = USBFS_UIF_TRANSFER;
+ return -1;
+ }
+ }
+
+ switch (res_state) {
+ case USB_PID_STALL:
+ USB_LOG_ERR("USB_PID_IN USB_PID_STALL\r\n");
+ g_chusb_hcd.current_token = 0;
+ urb->errorcode = -EPERM;
+ USBFS_HOST->INT_FG = USBFS_UIF_TRANSFER;
+ return -2;
+ case USB_PID_NAK:
+ g_chusb_hcd.current_token = 0;
+ if (g_chusb_hcd.current_pipe->ep_type == USB_ENDPOINT_TYPE_CONTROL) {
+ if (g_chusb_hcd.current_pipe_timeout > 0) {
+ urb->errorcode = -EAGAIN;
+ chusb_host_pipe_transfer(g_chusb_hcd.current_pipe, USB_PID_IN,
+ g_chusb_hcd.current_pipe->buffer, g_chusb_hcd.current_pipe->xferlen);
+ } else {
+ urb->errorcode = 0;
+ }
+ } else {
+ urb->errorcode = 0;
+ if (g_chusb_hcd.prv_get_zero == true) {
+ g_chusb_hcd.prv_get_zero = false;
+ g_chusb_hcd.current_token = 0;
+ urb->actual_length = g_chusb_hcd.current_pipe->xfrd;
+ USB_LOG_DBG("Normal endpoint get zero length package\r\n");
+ chusb_pipe_waitup(g_chusb_hcd.current_pipe, true);
+ } else {
+ USB_LOG_DBG("Normal endpoint in nak\r\n");
+ if (g_chusb_hcd.current_pipe->xferlen > 0) {
+ /**
+ * The data has not been transmitted completely
+ */
+ if (g_chusb_hcd.current_pipe->xfrd > 0) {
+ /**
+ * Data was transmitted last time, but this time NAK
+ */
+ if (g_chusb_hcd.current_pipe_timeout > 0) {
+ /**
+ * Retry in
+ */
+ chusb_host_pipe_transfer(g_chusb_hcd.current_pipe, USB_PID_IN,
+ g_chusb_hcd.current_pipe->buffer, g_chusb_hcd.current_pipe->xferlen);
+ } else {
+ /**
+ * Some data has been transferred
+ */
+ USB_LOG_WRN("The device has not finished sending all data, but the timeout is 0\r\n");
+ g_chusb_hcd.current_token = 0;
+ /**
+ * Update the actual send length
+ */
+ urb->actual_length = g_chusb_hcd.current_pipe->xfrd;
+ urb->errorcode = -EBUSY;
+ chusb_pipe_waitup(g_chusb_hcd.current_pipe, true);
+ }
+ } else {
+ /**
+ * The device did not send any data. We do not need to call a callback
+ */
+ urb->errorcode = -EBUSY;
+ g_chusb_hcd.current_token = 0;
+ urb->actual_length = 0;
+ chusb_pipe_waitup(g_chusb_hcd.current_pipe, false);
+ }
+ } else {
+ urb->errorcode = -EIO;
+ USB_LOG_ERR("xferlen == 0//should get zero package\r\n");
+ USBFS_HOST->INT_FG = USBFS_UIF_TRANSFER;
+ return -1;
+ }
+ }
+ }
+ break;
+ case USB_PID_ACK:
+ urb->errorcode = 0;
+ break;
+ case USB_PID_DATA0:
+ case USB_PID_DATA1:
+ if ((USBFS_HOST->INT_ST) & USBFS_UIS_TOG_OK) {
+ /*!< Data is OK */
+ rx_len = USBFS_HOST->RX_LEN;
+
+ urb->errorcode = 0;
+
+ g_chusb_hcd.current_pipe->xfrd += rx_len;
+
+ if (g_chusb_hcd.current_pipe->xferlen < rx_len) {
+ g_chusb_hcd.current_pipe->data_pid ^= 1;
+ USB_LOG_ERR("Please provide the correct data length parameter\r\n");
+ USBFS_HOST->INT_FG = USBFS_UIF_TRANSFER;
+ return -6;
+ }
+
+ g_chusb_hcd.current_pipe->xferlen -= rx_len;
+
+ if (g_chusb_hcd.current_pipe->ep_type == USB_ENDPOINT_TYPE_CONTROL) {
+ /*!< Ctrol endpoint */
+ /**
+ * Status stage
+ *
+ * Setup ---> out data ---> in status stage
+ */
+ if ((g_chusb_hcd.ep0_state == USB_EP0_STATE_IN_STATUS) && (rx_len == 0)) {
+ g_chusb_hcd.ep0_state = USB_EP0_STATE_SETUP;
+ urb->actual_length += rx_len;
+ USB_LOG_DBG("S-> O-> In-status stage: Out:%d\r\n", urb->actual_length - 8);
+
+ if (g_chusb_hcd.current_pipe->data_pid != 1) {
+ USB_LOG_ERR("S-> O-> In-status stage DATA PID Error//PID:%d\r\n", g_chusb_hcd.current_pipe->data_pid);
+ } else {
+ g_chusb_hcd.current_pipe->data_pid ^= 1;
+ }
+
+ chusb_pipe_waitup(g_chusb_hcd.current_pipe, true);
+ USBFS_HOST->INT_FG = USBFS_UIF_TRANSFER;
+ return 0;
+ }
+
+ /**
+ * Setup ---> in data ---> need generate next out status stage
+ */
+ if (g_chusb_hcd.ep0_state == USB_EP0_STATE_IN_DATA) {
+ USB_LOG_DBG("ep0 rx_len:%d\r\n", rx_len);
+ g_chusb_hcd.current_pipe->data_pid ^= 1;
+ if ((g_chusb_hcd.current_pipe->xfrd) > (urb->setup->wLength)) {
+ /**
+ * Error
+ */
+ USB_LOG_ERR("xfrd > setup->wLength\r\n");
+ USBFS_HOST->INT_FG = USBFS_UIF_TRANSFER;
+ return -3;
+ }
+
+ if (rx_len == 0 || (rx_len & (g_chusb_hcd.current_pipe->ep_mps - 1))) {
+ /**
+ * Receive a short package, in data has transfer completed
+ */
+ g_chusb_hcd.current_token = 0;
+ g_chusb_hcd.ep0_state = USB_EP0_STATE_OUT_STATUS;
+ /**
+ * generate next out status stage
+ */
+ chusb_control_pipe_init(g_chusb_hcd.current_pipe, urb->setup,
+ urb->transfer_buffer, urb->transfer_buffer_length);
+ } else {
+ /**
+ * Retry in
+ */
+ g_chusb_hcd.current_pipe->buffer += rx_len;
+ chusb_host_pipe_transfer(g_chusb_hcd.current_pipe, USB_PID_IN,
+ g_chusb_hcd.current_pipe->buffer, g_chusb_hcd.current_pipe->xferlen);
+ }
+ }
+ } else {
+ USB_LOG_DBG("ep%d rx_len:%d\r\n", (g_chusb_hcd.current_pipe->ep_addr & 0x0f), rx_len);
+ g_chusb_hcd.current_pipe->data_pid ^= 1;
+ if (rx_len == 0 || (rx_len & (g_chusb_hcd.current_pipe->ep_mps - 1))) {
+ /**
+ * Receive a short package, in data has transfer completed
+ */
+ g_chusb_hcd.current_token = 0;
+ urb->actual_length = g_chusb_hcd.current_pipe->xfrd;
+
+ if (g_chusb_hcd.prv_get_zero == true) {
+ g_chusb_hcd.prv_get_zero = false;
+ }
+
+ chusb_pipe_waitup(g_chusb_hcd.current_pipe, true);
+ } else {
+ /**
+ * Initiate In again
+ */
+ g_chusb_hcd.current_pipe->buffer += rx_len;
+
+ if (g_chusb_hcd.current_pipe->xferlen == 0) {
+ if (g_chusb_hcd.prv_get_zero == true) {
+ USB_LOG_ERR("g_chusb_hcd.prv_get_zero is always true\r\n");
+ g_chusb_hcd.prv_get_zero = false;
+ }
+ g_chusb_hcd.prv_get_zero = true;
+ }
+
+ chusb_host_pipe_transfer(g_chusb_hcd.current_pipe, USB_PID_IN,
+ g_chusb_hcd.current_pipe->buffer, g_chusb_hcd.current_pipe->xferlen);
+ }
+ }
+ } else {
+ /*!< Discard data out of synchronization */
+ USB_LOG_ERR("EP%dData out of sync %d\r\n",
+ (g_chusb_hcd.current_pipe->ep_addr & 0x0f), g_chusb_hcd.current_pipe->data_pid);
+ urb->errorcode = -EIO;
+ USBFS_HOST->INT_FG = USBFS_UIF_TRANSFER;
+ return -3;
+ }
+ break;
+ default:
+ break;
+ }
+
+ USBFS_HOST->INT_FG = USBFS_UIF_TRANSFER;
+ return 0;
+}
+
+void USBH_IRQHandler(void)
+{
+ INT_PRE_HANDLER();
+ volatile uint8_t intflag = 0;
+ volatile uint8_t res = 0;
+ intflag = USBFS_HOST->INT_FG;
+
+ if (intflag & USBFS_UIF_TRANSFER) {
+ /*!< Check the equipment response status */
+ res = (USBFS_HOST->INT_ST) & USBFS_UIS_ENDP_MASK;
+ /*!< Stop this transmission after successful transmission */
+ USBFS_HOST->HOST_EP_PID = 0x00;
+ switch (g_chusb_hcd.current_token) {
+ case USB_PID_SETUP:
+ case USB_PID_OUT:
+ if (chusb_outpipe_irq_handler(res) < 0) {
+ goto pipe_wait;
+ }
+ break;
+ case USB_PID_IN:
+ if (chusb_inpipe_irq_handler(res) < 0) {
+ goto pipe_wait;
+ }
+ break;
+ }
+ } else if (intflag & USBFS_UIF_DETECT) {
+ if (USBFS_HOST->MIS_ST & USBFS_UMS_DEV_ATTACH) {
+ if (g_chusb_hcd.has_reseted == 0x02) {
+ g_chusb_hcd.has_reseted = 0;
+ USB_LOG_INFO("USB bus reset and dev reconnect \r\n");
+ } else {
+ USB_LOG_INFO("Dev connect \r\n");
+ g_chusb_hcd.port_csc = 1;
+ g_chusb_hcd.port_pec = 1;
+ g_chusb_hcd.port_pe = 1;
+ usbh_roothub_thread_wakeup(1);
+ }
+ } else {
+ if (g_chusb_hcd.has_reseted == 0x01) {
+ g_chusb_hcd.has_reseted = 0x02;
+ if (g_chusb_hcd.main_pipe_using) {
+ g_chusb_hcd.main_pipe_using = false;
+ }
+ } else {
+ USB_LOG_INFO("Dev remove \r\n");
+ /**
+ * Device remove
+ * Disable port and stop send sof
+ */
+ USBFS_HOST->HOST_SETUP &= ~USBFS_UH_SOF_EN;
+ USBFS_HOST->HOST_CTRL &= ~USBFS_UH_PORT_EN;
+ if (g_chusb_hcd.main_pipe_using) {
+ g_chusb_hcd.main_pipe_using = false;
+ }
+ g_chusb_hcd.port_csc = 1;
+ g_chusb_hcd.port_pec = 1;
+ g_chusb_hcd.port_pe = 0;
+ for (uint8_t index = 0; index < CONFIG_USBHOST_PIPE_NUM; index++) {
+ for (uint8_t j = 0; j < 2; j++) {
+ struct chusb_pipe *pipe = &g_chusb_hcd.pipe_pool[index][j];
+ struct usbh_urb *urb = pipe->urb;
+ if (pipe->waiter) {
+ pipe->waiter = false;
+ urb->errorcode = -ESHUTDOWN;
+ usb_osal_sem_give(pipe->waitsem);
+ }
+ }
+ }
+ usbh_roothub_thread_wakeup(1);
+ }
+ }
+ USBFS_HOST->INT_FG = USBFS_UIF_DETECT;
+ } else {
+ USB_LOG_INFO("Unkonwn \r\n");
+ USBFS_HOST->INT_FG = intflag;
+ }
+ INT_POST_HANDLER();
+ return;
+pipe_wait:
+ /**
+ * enerally, only errors can arrive here.
+ * After testing, most cases arrive here because of the problem of the DATA PID,
+ * but the transmission will be completed correctly next time.
+ */
+ chusb_pipe_waitup(g_chusb_hcd.current_pipe, true);
+ INT_POST_HANDLER();
+}
diff --git a/port/ch32/usb_hc_usbhs.c b/port/ch32/usb_hc_usbhs.c
new file mode 100644
index 00000000..19f038ca
--- /dev/null
+++ b/port/ch32/usb_hc_usbhs.c
@@ -0,0 +1,1341 @@
+#include "usbh_core.h"
+#include "usbh_hub.h"
+#include "usb_ch32_usbhs_reg.h"
+
+#ifndef USBH_IRQHandler
+#define USBH_IRQHandler USB2_IRQHandler
+#endif
+
+#ifndef CONFIG_USBHOST_PIPE_NUM
+#define CONFIG_USBHOST_PIPE_NUM 8
+#endif
+
+#ifndef USBHS_MAX_PACKET_SIZE
+#define USBHS_MAX_PACKET_SIZE 1024
+#endif
+
+#define HIGH_SP_HIGH_BAND_Pos 11
+#define HIGH_SP_HIGH_BAND_Mask (0x03 << HIGH_SP_HIGH_BAND_Pos)
+#define HIGH_SP_HIGH_BAND_1 (0x00 << HIGH_SP_HIGH_BAND_Pos)
+#define HIGH_SP_HIGH_BAND_2 (0x01 << HIGH_SP_HIGH_BAND_Pos)
+#define HIGH_SP_HIGH_BAND_3 (0x02 << HIGH_SP_HIGH_BAND_Pos)
+
+typedef enum {
+ USB_EP0_STATE_SETUP = 0x0, /**< SETUP DATA */
+ USB_EP0_STATE_IN_DATA, /**< IN DATA */
+ USB_EP0_STATE_IN_STATUS, /**< IN status */
+ USB_EP0_STATE_OUT_DATA, /**< OUT DATA */
+ USB_EP0_STATE_OUT_STATUS, /**< OUT status */
+} ep0_state_t;
+
+struct chusb_pipe {
+ uint8_t dev_addr;
+ uint8_t ep_addr;
+ uint8_t ep_type;
+ uint8_t ep_interval;
+ uint8_t speed;
+ uint16_t ep_mps;
+ bool inuse;
+ uint32_t xfrd;
+ volatile bool waiter;
+
+ uint32_t xferlen;
+ uint8_t *buffer;
+ uint8_t data_pid;
+ uint8_t trans_num;
+
+ usb_osal_sem_t waitsem;
+ struct usbh_hubport *hport;
+ struct usbh_urb *urb;
+};
+
+struct chusb_hcd {
+ volatile bool port_csc;
+ volatile bool port_pec;
+ volatile bool port_pe;
+ volatile uint8_t current_token;
+ volatile uint8_t ep0_state;
+ volatile bool prv_get_zero;
+ volatile bool prv_set_zero;
+ volatile bool main_pipe_using;
+ uint32_t current_pipe_timeout;
+ struct chusb_pipe *current_pipe;
+ struct chusb_pipe pipe_pool[CONFIG_USBHOST_PIPE_NUM][2]; /* Support Bidirectional ep */
+} g_chusb_hcd;
+
+static inline void SET_UH_RX_CTRL_BIT(uint8_t bit)
+{
+ if ((USBHS_HOST->HOST_RX_CTRL & USBHS_UH_R_TOG_AUTO) != 0) {
+ /**
+ * USBHS_UH_R_TOG canot write
+ */
+ if ((bit & (USBHS_UH_R_TOG_1 | USBHS_UH_R_TOG_2 | USBHS_UH_R_TOG_3)) != 0) {
+ /**
+ * bit contains USBHS_UH_R_TOG
+ */
+ USBHS_HOST->HOST_RX_CTRL &= ~(USBHS_UH_R_TOG_AUTO);
+ USBHS_HOST->HOST_RX_CTRL |= (USBHS_UH_R_TOG_AUTO | bit);
+ } else {
+ USBHS_HOST->HOST_RX_CTRL |= bit;
+ }
+ } else {
+ USBHS_HOST->HOST_RX_CTRL |= bit;
+ }
+}
+
+static inline void SET_UH_TX_CTRL_BIT(uint8_t bit)
+{
+ if ((USBHS_HOST->HOST_TX_CTRL & USBHS_UH_T_TOG_AUTO) != 0) {
+ /**
+ * USBFS_UH_T_TOG canot write
+ */
+ if ((bit & (USBHS_UH_T_TOG_1 | USBHS_UH_T_TOG_2 | USBHS_UH_T_TOG_3)) != 0) {
+ /**
+ * bit contains USBFS_UH_T_TOG
+ */
+ USBHS_HOST->HOST_TX_CTRL &= ~(USBHS_UH_T_TOG_AUTO);
+ USBHS_HOST->HOST_TX_CTRL |= (USBHS_UH_T_TOG_AUTO | bit);
+ } else {
+ USBHS_HOST->HOST_TX_CTRL |= bit;
+ }
+ } else {
+ USBHS_HOST->HOST_TX_CTRL |= bit;
+ }
+}
+
+static inline void CLEAR_UH_TX_CTRL_BIT(uint8_t bit)
+{
+ if ((USBHS_HOST->HOST_TX_CTRL & USBHS_UH_T_TOG_AUTO) != 0) {
+ /**
+ * USBFS_UH_T_TOG canot write
+ */
+ if ((bit & (USBHS_UH_T_TOG_1 | USBHS_UH_T_TOG_2 | USBHS_UH_T_TOG_3)) != 0) {
+ /**
+ * bit contains USBFS_UH_T_TOG
+ */
+ USBHS_HOST->HOST_TX_CTRL &= ~(USBHS_UH_T_TOG_AUTO);
+ USBHS_HOST->HOST_TX_CTRL &= ~(bit);
+ USBHS_HOST->HOST_TX_CTRL |= USBHS_UH_T_TOG_AUTO;
+ } else {
+ USBHS_HOST->HOST_TX_CTRL &= ~(bit);
+ }
+ } else {
+ USBHS_HOST->HOST_TX_CTRL &= ~(bit);
+ }
+}
+
+static inline void CLEAR_UH_RX_CTRL_BIT(uint8_t bit)
+{
+ if ((USBHS_HOST->HOST_RX_CTRL & USBHS_UH_R_TOG_AUTO) != 0) {
+ /**
+ * USBFS_UH_R_TOG canot write
+ */
+ if ((bit & (USBHS_UH_R_TOG_1 | USBHS_UH_R_TOG_2 | USBHS_UH_R_TOG_3)) != 0) {
+ /**
+ * bit contains USBFS_UH_R_TOG
+ */
+ USBHS_HOST->HOST_RX_CTRL &= ~(USBHS_UH_R_TOG_AUTO);
+ USBHS_HOST->HOST_RX_CTRL &= ~(bit);
+ USBHS_HOST->HOST_RX_CTRL |= USBHS_UH_R_TOG_AUTO;
+ } else {
+ USBHS_HOST->HOST_RX_CTRL &= ~(bit);
+ }
+ } else {
+ USBHS_HOST->HOST_RX_CTRL &= ~(bit);
+ }
+}
+
+static inline void SET_UH_RX_CTRL(uint8_t value)
+{
+ USBHS_HOST->HOST_RX_CTRL = 0;
+ USBHS_HOST->HOST_RX_CTRL = value;
+ USB_LOG_DBG("USBHS_HOST->HOST_RX_CTRL:%02x\r\n", USBHS_HOST->HOST_RX_CTRL);
+}
+
+static inline void SET_UH_TX_CTRL(uint8_t value)
+{
+ USBHS_HOST->HOST_TX_CTRL = 0;
+ USBHS_HOST->HOST_TX_CTRL = value;
+ USB_LOG_DBG("USBHS_HOST->HOST_TX_CTRL:%02x\r\n", USBHS_HOST->HOST_TX_CTRL);
+}
+
+static inline void INT_PRE_HANDLER(void)
+{
+#if defined(CH581) || defined(CH582) || defined(CH583) || defined(CH571) || defined(CH572) || defined(CH573)
+#else
+ asm("csrrw sp,mscratch,sp");
+ extern void rt_interrupt_enter(void);
+ rt_interrupt_enter();
+#endif
+}
+
+static inline void INT_POST_HANDLER(void)
+{
+#if defined(CH581) || defined(CH582) || defined(CH583) || defined(CH571) || defined(CH572) || defined(CH573)
+#else
+ extern void rt_interrupt_leave(void);
+ rt_interrupt_leave();
+ asm("csrrw sp,mscratch,sp");
+#endif
+}
+
+static int8_t chusb_host_pipe_transfer(struct chusb_pipe *pipe, uint8_t pid, uint8_t *data, uint32_t len)
+{
+ /*!< Updata current transfer pid */
+ g_chusb_hcd.current_token = pid;
+ /*!< Updata curretn pipe */
+ g_chusb_hcd.current_pipe = pipe;
+ /*!< Updata curretn pipe timeout */
+ g_chusb_hcd.current_pipe_timeout = pipe->urb->timeout;
+ /*!< Updata main pipe using flag */
+ g_chusb_hcd.main_pipe_using = true;
+
+ if (data == NULL && len > 0) {
+ return -1;
+ }
+
+ if (pid == USB_PID_SETUP) {
+ /*!< Record the data len */
+ g_chusb_hcd.current_pipe->xferlen = len;
+
+ if ((uint32_t)data & 0x03) {
+ USB_LOG_INFO("SETUP DMA address is not align \r\n");
+ return -3;
+ }
+
+ USBHS_HOST->HOST_TX_DMA = (uint16_t)(uint32_t)data;
+
+ /*!< Record the data buffer address */
+ pipe->buffer = data;
+
+ if (len > pipe->ep_mps) {
+ len = pipe->ep_mps;
+ }
+
+ USBHS_HOST->HOST_TX_LEN = len;
+ USBHS_HOST->HOST_EP_PID = pid << 4 | (pipe->ep_addr & 0x0f);
+
+ } else if (pid == USB_PID_OUT) {
+ /*!< Record the data len */
+ g_chusb_hcd.current_pipe->xferlen = len;
+
+ if (len == 0) {
+ USBHS_HOST->HOST_TX_LEN = len;
+ USBHS_HOST->HOST_EP_PID = pid << 4 | (pipe->ep_addr & 0x0f);
+ return 0;
+ }
+
+ if ((uint32_t)data & 0x03) {
+ USB_LOG_INFO("OUT DMA address is not align \r\n");
+ return -3;
+ }
+
+ USBHS_HOST->HOST_TX_DMA = (uint16_t)(uint32_t)data;
+
+ /*!< Record the data buffer address */
+ pipe->buffer = data;
+
+ if (len > pipe->ep_mps) {
+ len = pipe->ep_mps;
+ }
+
+ USBHS_HOST->HOST_TX_LEN = len;
+ USBHS_HOST->HOST_EP_PID = pid << 4 | (pipe->ep_addr & 0x0f);
+
+ } else if (pid == USB_PID_IN) {
+ /*!< Record the data len */
+ g_chusb_hcd.current_pipe->xferlen = len;
+
+ if (len == 0) {
+ /*!< Want get 0 length data */
+ } else {
+ if ((uint32_t)data & 0x03) {
+ USB_LOG_INFO("IN DMA address is not align \r\n");
+ return -3;
+ }
+ USBHS_HOST->HOST_RX_DMA = (uint16_t)(uint32_t)data;
+ pipe->buffer = data;
+ }
+
+ USBHS_HOST->HOST_EP_PID = pid << 4 | (pipe->ep_addr & 0x0f);
+ }
+ return 0;
+}
+
+static void chusb_control_pipe_init(struct chusb_pipe *pipe, struct usb_setup_packet *setup, uint8_t *buffer, uint32_t buflen)
+{
+ if (g_chusb_hcd.ep0_state == USB_EP0_STATE_SETUP) {
+ /**
+ * Setup is distributed as DATA0
+ */
+ SET_UH_TX_CTRL(USBHS_UH_T_TOG_AUTO);
+ pipe->data_pid = 0;
+ chusb_host_pipe_transfer(pipe, USB_PID_SETUP, (uint8_t *)setup, 8);
+ } else if (g_chusb_hcd.ep0_state == USB_EP0_STATE_IN_DATA) {
+ if (pipe->data_pid != 1) {
+ USB_LOG_ERR("IN_DATA PID Error\r\n");
+ }
+ SET_UH_RX_CTRL(USBHS_UH_R_TOG_AUTO | USBHS_UH_R_TOG_1);
+ chusb_host_pipe_transfer(pipe, USB_PID_IN, buffer, buflen);
+ } else if (g_chusb_hcd.ep0_state == USB_EP0_STATE_OUT_DATA) {
+ if (pipe->data_pid != 1) {
+ USB_LOG_ERR("OUT_DATA PID Error\r\n");
+ }
+ chusb_host_pipe_transfer(pipe, USB_PID_OUT, buffer, buflen);
+ } else if (g_chusb_hcd.ep0_state == USB_EP0_STATE_IN_STATUS) {
+ /**
+ * Status stage must be DATA1
+ */
+ // SET_UH_RX_CTRL_BIT(USBFS_UH_R_TOG);
+ pipe->data_pid = 1;
+ SET_UH_RX_CTRL(USBHS_UH_R_TOG_AUTO | USBHS_UH_R_TOG_1);
+ chusb_host_pipe_transfer(pipe, USB_PID_IN, NULL, 0);
+ } else if (g_chusb_hcd.ep0_state == USB_EP0_STATE_OUT_STATUS) {
+ /**
+ * Status stage must be DATA1
+ */
+ // SET_UH_TX_CTRL_BIT(USBFS_UH_T_TOG);
+ pipe->data_pid = 1;
+ SET_UH_TX_CTRL(USBHS_UH_T_TOG_AUTO | USBHS_UH_T_TOG_1);
+ chusb_host_pipe_transfer(pipe, USB_PID_OUT, NULL, 0);
+ }
+}
+
+static void chusb_bulk_pipe_init(struct chusb_pipe *pipe, uint8_t *buffer, uint32_t buflen)
+{
+ if (pipe->ep_type != USB_ENDPOINT_TYPE_BULK) {
+ USB_LOG_ERR("Endpoint type is not BULK\r\n");
+ return;
+ }
+
+ if (pipe->ep_addr & 0x80) {
+ /*!< IN */
+ g_chusb_hcd.current_token = USB_PID_IN;
+ if (pipe->data_pid == 1) {
+ SET_UH_RX_CTRL(USBHS_UH_R_TOG_AUTO | USBHS_UH_R_TOG_1);
+ } else {
+ SET_UH_RX_CTRL(USBHS_UH_R_TOG_AUTO);
+ }
+ } else {
+ /*!< OUT */
+ g_chusb_hcd.current_token = USB_PID_OUT;
+ if (pipe->data_pid == 1) {
+ SET_UH_TX_CTRL(USBHS_UH_T_TOG_AUTO | USBHS_UH_T_TOG_1);
+ } else {
+ SET_UH_TX_CTRL(USBHS_UH_T_TOG_AUTO);
+ }
+ }
+
+ chusb_host_pipe_transfer(pipe, g_chusb_hcd.current_token, buffer, buflen);
+}
+
+static void chusb_intr_pipe_init(struct chusb_pipe *pipe, uint8_t *buffer, uint32_t buflen)
+{
+ if (pipe->ep_type != USB_ENDPOINT_TYPE_INTERRUPT) {
+ USB_LOG_ERR("Endpoint type is not INTERRUPT\r\n");
+ return;
+ }
+
+ if (pipe->ep_addr & 0x80) {
+ /*!< IN */
+ g_chusb_hcd.current_token = USB_PID_IN;
+
+ if (pipe->trans_num > 1) {
+ USB_LOG_WRN("Not supported temporarily\r\n");
+ if (pipe->trans_num == 2) {
+ } else if (pipe->trans_num == 3) {
+ }
+ } else {
+ if (pipe->data_pid == 1) {
+ SET_UH_RX_CTRL(USBHS_UH_R_TOG_AUTO | USBHS_UH_R_TOG_1);
+ } else {
+ SET_UH_RX_CTRL(USBHS_UH_R_TOG_AUTO);
+ }
+ }
+ } else {
+ /*!< OUT */
+ g_chusb_hcd.current_token = USB_PID_OUT;
+ if (pipe->trans_num > 1) {
+ USB_LOG_WRN("Not supported temporarily\r\n");
+ if (pipe->trans_num == 2) {
+ } else if (pipe->trans_num == 3) {
+ }
+ } else {
+ if (pipe->data_pid == 1) {
+ SET_UH_TX_CTRL(USBHS_UH_T_TOG_AUTO | USBHS_UH_T_TOG_1);
+ } else {
+ SET_UH_TX_CTRL(USBHS_UH_T_TOG_AUTO);
+ }
+ }
+ }
+
+ chusb_host_pipe_transfer(pipe, g_chusb_hcd.current_token, buffer, buflen);
+}
+
+static void chusb_iso_pipe_init(struct chusb_pipe *pipe, uint8_t *buffer, uint32_t buflen)
+{
+ if (pipe->ep_type != USB_ENDPOINT_TYPE_ISOCHRONOUS) {
+ USB_LOG_ERR("Endpoint type is not ISOCHRONOUS\r\n");
+ return;
+ }
+
+ if (pipe->ep_addr & 0x80) {
+ /*!< IN */
+ g_chusb_hcd.current_token = USB_PID_IN;
+ if (pipe->trans_num > 1) {
+ /**
+ * High speed and high bandwidth isochronous endpoint
+ */
+ USB_LOG_WRN("Not supported temporarily\r\n");
+#if 0
+ if (pipe->trans_num == 2) {
+ SET_UH_RX_CTRL(USBHS_UH_R_TOG_AUTO | USBHS_UH_R_TOG_1 | USBHS_UH_R_RES_NO);
+ } else if (pipe->trans_num == 3) {
+ SET_UH_RX_CTRL(USBHS_UH_R_TOG_AUTO | USBHS_UH_R_TOG_2 | USBHS_UH_R_RES_NO);
+ }
+#endif
+ } else {
+ SET_UH_RX_CTRL(USBHS_UH_R_TOG_AUTO | USBHS_UH_R_RES_NO);
+ chusb_host_pipe_transfer(pipe, g_chusb_hcd.current_token, buffer, buflen);
+ return;
+ }
+ } else {
+ /*!< OUT */
+ g_chusb_hcd.current_token = USB_PID_OUT;
+ if (pipe->trans_num > 1) {
+ /**
+ * High speed and high bandwidth isochronous endpoint
+ */
+ USB_LOG_WRN("Not supported temporarily\r\n");
+#if 0
+ if (pipe->trans_num == 2) {
+ if (buflen < pipe->ep_mps) {
+ SET_UH_TX_CTRL(USBHS_UH_T_TOG_AUTO | USBHS_UH_T_RES_NO);
+ } else {
+ uint32_t send_len = buflen;
+ while (send_len > pipe->ep_mps) {
+ /**
+ * First send a MDATA
+ */
+ SET_UH_TX_CTRL(USBHS_UH_T_TOG_3 | USBHS_UH_T_RES_NO);
+ chusb_host_pipe_transfer(pipe, g_chusb_hcd.current_token, buffer, send_len);
+ send_len -= pipe->ep_mps;
+ SET_UH_TX_CTRL(USBHS_UH_T_TOG_1 | USBHS_UH_T_RES_NO);
+ chusb_host_pipe_transfer(pipe, g_chusb_hcd.current_token, (buffer + pipe->ep_mps), send_len);
+ if (send_len > pipe->ep_mps) {
+ send_len -= pipe->ep_mps;
+ }
+ }
+ }
+ return;
+ } else if (pipe->trans_num == 3) {
+ if (buflen < pipe->ep_mps) {
+ SET_UH_TX_CTRL(USBHS_UH_T_TOG_AUTO | USBHS_UH_T_RES_NO);
+ } else if (buflen < (2 * pipe->ep_mps)) {
+ SET_UH_TX_CTRL(USBHS_UH_T_TOG_3 | USBHS_UH_T_RES_NO);
+ chusb_host_pipe_transfer(pipe, g_chusb_hcd.current_token, buffer, buflen);
+ SET_UH_TX_CTRL(USBHS_UH_T_TOG_1 | USBHS_UH_T_RES_NO);
+ chusb_host_pipe_transfer(pipe, g_chusb_hcd.current_token, (buffer + pipe->ep_mps), (buflen - pipe->ep_mps));
+ return;
+ } else {
+ uint32_t send_len = buflen;
+ while (send_len > pipe->ep_mps) {
+ /**
+ * First send a MDATA
+ */
+ SET_UH_TX_CTRL(USBHS_UH_T_TOG_3 | USBHS_UH_T_RES_NO);
+ chusb_host_pipe_transfer(pipe, g_chusb_hcd.current_token, buffer, send_len);
+ send_len -= pipe->ep_mps;
+ SET_UH_TX_CTRL(USBHS_UH_T_TOG_3 | USBHS_UH_T_RES_NO);
+ chusb_host_pipe_transfer(pipe, g_chusb_hcd.current_token, (buffer + pipe->ep_mps), send_len);
+ send_len -= pipe->ep_mps;
+ SET_UH_TX_CTRL(USBHS_UH_T_TOG_2 | USBHS_UH_T_RES_NO);
+ chusb_host_pipe_transfer(pipe, g_chusb_hcd.current_token, (buffer + pipe->ep_mps), send_len);
+ if (send_len > pipe->ep_mps) {
+ send_len -= pipe->ep_mps;
+ }
+ }
+ }
+ }
+#endif
+ } else {
+ SET_UH_TX_CTRL(USBHS_UH_T_TOG_AUTO | USBHS_UH_T_RES_NO);
+ chusb_host_pipe_transfer(pipe, g_chusb_hcd.current_token, buffer, buflen);
+ return;
+ }
+ }
+}
+
+static void chusbh_set_self_speed(uint8_t speed)
+{
+ if (speed == USB_SPEED_HIGH) {
+ USBHS_HOST->CONTROL = (USBHS_HOST->CONTROL & ~USBHS_SPEED_MASK) | USBHS_HIGH_SPEED;
+ } else if (speed == USB_SPEED_FULL) {
+ USBHS_HOST->CONTROL = (USBHS_HOST->CONTROL & ~USBHS_SPEED_MASK) | USBHS_FULL_SPEED;
+ } else {
+ USBHS_HOST->CONTROL = (USBHS_HOST->CONTROL & ~USBHS_SPEED_MASK) | USBHS_LOW_SPEED;
+ }
+}
+
+static int usbh_reset_port(const uint8_t port)
+{
+ g_chusb_hcd.port_pe = 0;
+ /*!< Set dev add 0 */
+ USBHS_HOST->DEV_AD = (0x00 & 0x7F);
+ chusbh_set_self_speed(USB_SPEED_HIGH);
+
+ /*!< Start reset */
+ USBHS_HOST->HOST_CTRL |= USBHS_SEND_BUS_RESET;
+ usb_osal_msleep(30);
+ /*!< Stop reset */
+ USBHS_HOST->HOST_CTRL &= ~USBHS_SEND_BUS_RESET;
+ usb_osal_msleep(20);
+ // USBHS_HOST->HOST_CTRL |= USBHS_SEND_SOF_EN;
+ g_chusb_hcd.port_pe = 1;
+ return 0;
+}
+
+static uint8_t usbh_get_port_speed(const uint8_t port)
+{
+ (void)port;
+ uint8_t speed = 0;
+ if (USBHS_HOST->MIS_ST & 0x02) {
+ if ((USBHS_HOST->HOST_CTRL & USBHS_SEND_SOF_EN) == 0) {
+ speed = USBHS_HOST->SPEED_TYPE & USBSPEED_MASK;
+
+ if (speed == 0x02) {
+ speed = USB_SPEED_LOW;
+ } else if (speed == 0x00) {
+ speed = USB_SPEED_FULL;
+ } else if (speed == 0x01) {
+ speed = USB_SPEED_HIGH;
+ }
+ }
+ }
+
+ USBHS_HOST->HOST_CTRL |= USBHS_SEND_SOF_EN;
+ return speed;
+}
+
+__WEAK void usb_hc_low_level_init(void)
+{
+}
+
+int usb_hc_init(void)
+{
+ memset(&g_chusb_hcd, 0, sizeof(struct chusb_hcd));
+
+ for (uint8_t i = 0; i < CONFIG_USBHOST_PIPE_NUM; i++) {
+ g_chusb_hcd.pipe_pool[i][0].waitsem = usb_osal_sem_create(0);
+ g_chusb_hcd.pipe_pool[i][1].waitsem = usb_osal_sem_create(0);
+ }
+
+ usb_hc_low_level_init();
+
+ /* Reset USB module */
+ USBHS_HOST->CONTROL = USBHS_ALL_CLR | USBHS_FORCE_RST;
+ static volatile uint32_t wait_ct = 50000;
+ while (wait_ct--) {
+ }
+ USBHS_HOST->CONTROL = 0;
+
+ /* Initialize USB host configuration */
+ USBHS_HOST->CONTROL = USBHS_HOST_MODE | USBHS_HIGH_SPEED | USBHS_INT_BUSY_EN | USBHS_DMA_EN;
+ USBHS_HOST->HOST_EP_CONFIG = USBHS_HOST_TX_EN | USBHS_HOST_RX_EN;
+ USBHS_HOST->HOST_CTRL = USBHS_PHY_SUSPENDM;
+ USBHS_HOST->HOST_RX_MAX_LEN = USBHS_MAX_PACKET_SIZE;
+
+ USBHS_HOST->HOST_RX_CTRL = 0;
+ USBHS_HOST->HOST_TX_CTRL = 0;
+ USBHS_HOST->INT_FG = 0xFF;
+ USBHS_HOST->INT_EN = USBHS_TRANSFER_EN | USBHS_DETECT_EN;
+ return 0;
+}
+
+int usbh_roothub_control(struct usb_setup_packet *setup, uint8_t *buf)
+{
+ uint8_t nports;
+ uint8_t port;
+ uint32_t status;
+
+ nports = CONFIG_USBHOST_MAX_RHPORTS;
+ port = setup->wIndex;
+ if (setup->bmRequestType & USB_REQUEST_RECIPIENT_DEVICE) {
+ switch (setup->bRequest) {
+ case HUB_REQUEST_CLEAR_FEATURE:
+ switch (setup->wValue) {
+ case HUB_FEATURE_HUB_C_LOCALPOWER:
+ break;
+ case HUB_FEATURE_HUB_C_OVERCURRENT:
+ break;
+ default:
+ return -EPIPE;
+ }
+ break;
+ case HUB_REQUEST_SET_FEATURE:
+ switch (setup->wValue) {
+ case HUB_FEATURE_HUB_C_LOCALPOWER:
+ break;
+ case HUB_FEATURE_HUB_C_OVERCURRENT:
+ break;
+ default:
+ return -EPIPE;
+ }
+ break;
+ case HUB_REQUEST_GET_DESCRIPTOR:
+ break;
+ case HUB_REQUEST_GET_STATUS:
+ memset(buf, 0, 4);
+ break;
+ default:
+ break;
+ }
+ } else if (setup->bmRequestType & USB_REQUEST_RECIPIENT_OTHER) {
+ switch (setup->bRequest) {
+ case HUB_REQUEST_CLEAR_FEATURE:
+ if (!port || port > nports) {
+ return -EPIPE;
+ }
+
+ switch (setup->wValue) {
+ case HUB_PORT_FEATURE_ENABLE:
+ break;
+ case HUB_PORT_FEATURE_SUSPEND:
+ case HUB_PORT_FEATURE_C_SUSPEND:
+ break;
+ case HUB_PORT_FEATURE_POWER:
+ break;
+ case HUB_PORT_FEATURE_C_CONNECTION:
+ g_chusb_hcd.port_csc = 0;
+ break;
+ case HUB_PORT_FEATURE_C_ENABLE:
+ g_chusb_hcd.port_pec = 0;
+ break;
+ case HUB_PORT_FEATURE_C_OVER_CURREN:
+ break;
+ case HUB_PORT_FEATURE_C_RESET:
+ break;
+ default:
+ return -EPIPE;
+ }
+ break;
+ case HUB_REQUEST_SET_FEATURE:
+ if (!port || port > nports) {
+ return -EPIPE;
+ }
+
+ switch (setup->wValue) {
+ case HUB_PORT_FEATURE_SUSPEND:
+ break;
+ case HUB_PORT_FEATURE_POWER:
+ break;
+ case HUB_PORT_FEATURE_RESET:
+ usbh_reset_port(port);
+ break;
+
+ default:
+ return -EPIPE;
+ }
+ break;
+ case HUB_REQUEST_GET_STATUS:
+ if (!port || port > nports) {
+ return -EPIPE;
+ }
+
+ status = 0;
+ if (g_chusb_hcd.port_csc) {
+ status |= (1 << HUB_PORT_FEATURE_C_CONNECTION);
+ }
+ if (g_chusb_hcd.port_pec) {
+ status |= (1 << HUB_PORT_FEATURE_C_ENABLE);
+ }
+
+ if (g_chusb_hcd.port_pe) {
+ status |= (1 << HUB_PORT_FEATURE_CONNECTION);
+ status |= (1 << HUB_PORT_FEATURE_ENABLE);
+ if (usbh_get_port_speed(port) == USB_SPEED_LOW) {
+ status |= (1 << HUB_PORT_FEATURE_LOWSPEED);
+ } else if (usbh_get_port_speed(port) == USB_SPEED_HIGH) {
+ status |= (1 << HUB_PORT_FEATURE_HIGHSPEED);
+ }
+ }
+
+ memcpy(buf, &status, 4);
+ break;
+ default:
+ break;
+ }
+ }
+ return 0;
+}
+
+int usbh_ep0_pipe_reconfigure(usbh_pipe_t pipe, uint8_t dev_addr, uint8_t ep_mps, uint8_t speed)
+{
+ struct chusb_pipe *ppipe = (struct chusb_pipe *)pipe;
+
+ ppipe->dev_addr = dev_addr;
+ ppipe->ep_mps = ep_mps;
+ ppipe->speed = speed;
+
+ if (speed == USB_SPEED_HIGH) {
+ USB_LOG_INFO("ep0 reconfigure USB_SPEED_HIGH \r\n");
+ chusbh_set_self_speed(USB_SPEED_HIGH);
+ } else if (speed == USB_SPEED_FULL) {
+ USB_LOG_INFO("ep0 reconfigure USB_SPEED_FULL \r\n");
+ chusbh_set_self_speed(USB_SPEED_FULL);
+ } else if (speed == USB_SPEED_LOW) {
+ USB_LOG_INFO("ep0 reconfigure USB_SPEED_LOW \r\n");
+ chusbh_set_self_speed(USB_SPEED_LOW);
+ }
+ USBHS_HOST->DEV_AD = dev_addr & 0x7f;
+ return 0;
+}
+
+int usbh_pipe_alloc(usbh_pipe_t *pipe, const struct usbh_endpoint_cfg *ep_cfg)
+{
+ struct chusb_pipe *ppipe;
+ uint8_t ep_idx;
+ usb_osal_sem_t waitsem;
+
+ ep_idx = ep_cfg->ep_addr & 0x7f;
+
+ if (ep_idx > CONFIG_USBHOST_PIPE_NUM) {
+ return -ENOMEM;
+ }
+
+ if (ep_cfg->ep_addr & 0x80) {
+ ppipe = &g_chusb_hcd.pipe_pool[ep_idx][1];
+ } else {
+ ppipe = &g_chusb_hcd.pipe_pool[ep_idx][0];
+ }
+
+ /* store variables */
+ waitsem = ppipe->waitsem;
+
+ memset(ppipe, 0, sizeof(struct chusb_pipe));
+
+ ppipe->ep_addr = ep_cfg->ep_addr;
+ ppipe->ep_type = ep_cfg->ep_type;
+ ppipe->ep_mps = ep_cfg->ep_mps;
+ ppipe->ep_interval = ep_cfg->ep_interval;
+ ppipe->speed = ep_cfg->hport->speed;
+ ppipe->dev_addr = ep_cfg->hport->dev_addr;
+ ppipe->hport = ep_cfg->hport;
+
+ if (ep_cfg->ep_type == USB_ENDPOINT_TYPE_CONTROL) {
+ } else {
+ if (ppipe->speed == USB_SPEED_HIGH) {
+ if ((ep_cfg->ep_type == USB_ENDPOINT_TYPE_ISOCHRONOUS) ||
+ (ep_cfg->ep_type == USB_ENDPOINT_TYPE_INTERRUPT)) {
+ if ((ppipe->ep_mps & HIGH_SP_HIGH_BAND_Mask) != 0) {
+ /**
+ * High speed and high bandwidth isochronous endpoint
+ */
+ if ((ppipe->ep_mps & HIGH_SP_HIGH_BAND_Mask) == HIGH_SP_HIGH_BAND_2) {
+ ppipe->trans_num = 2;
+ } else if ((ppipe->ep_mps & HIGH_SP_HIGH_BAND_Mask) == HIGH_SP_HIGH_BAND_3) {
+ ppipe->trans_num = 3;
+ }
+ } else {
+ ppipe->trans_num = 1;
+ }
+ }
+ } else if (ppipe->speed == USB_SPEED_FULL) {
+ } else if (ppipe->speed == USB_SPEED_LOW) {
+ }
+ }
+ /* restore variable */
+ ppipe->inuse = true;
+ ppipe->waitsem = waitsem;
+
+ *pipe = (usbh_pipe_t)ppipe;
+ return 0;
+}
+
+int usbh_pipe_free(usbh_pipe_t pipe)
+{
+ return 0;
+}
+
+int usbh_submit_urb(struct usbh_urb *urb)
+{
+ struct chusb_pipe *pipe;
+ size_t flags;
+ int ret = 0;
+
+ if (!urb) {
+ USB_LOG_INFO("urb is null \r\n");
+ return -EINVAL;
+ }
+
+ pipe = urb->pipe;
+
+ if (!pipe) {
+ USB_LOG_INFO("pipe is null \r\n");
+ return -EINVAL;
+ }
+
+ if (!pipe->hport->connected) {
+ USB_LOG_INFO("!pipe->hport->connected \r\n");
+ return -ENODEV;
+ }
+
+ if (pipe->urb) {
+ USB_LOG_INFO("pipe->urb is not null\r\n");
+ return -EBUSY;
+ }
+
+ if (g_chusb_hcd.main_pipe_using == true) {
+ USB_LOG_INFO("usbh_submit_urb//main pipe is using\r\n");
+ return -EBUSY;
+ }
+
+ flags = usb_osal_enter_critical_section();
+
+ pipe->waiter = false;
+ pipe->xfrd = 0;
+ pipe->urb = urb;
+ urb->errorcode = -EBUSY;
+ urb->actual_length = 0;
+
+ if (urb->timeout > 0) {
+ pipe->waiter = true;
+ }
+ usb_osal_leave_critical_section(flags);
+
+ switch (pipe->ep_type) {
+ case USB_ENDPOINT_TYPE_CONTROL:
+ g_chusb_hcd.ep0_state = USB_EP0_STATE_SETUP;
+ chusb_control_pipe_init(pipe, urb->setup, urb->transfer_buffer, urb->transfer_buffer_length);
+ break;
+ case USB_ENDPOINT_TYPE_BULK:
+ chusb_bulk_pipe_init(pipe, urb->transfer_buffer, urb->transfer_buffer_length);
+ break;
+ case USB_ENDPOINT_TYPE_INTERRUPT:
+ chusb_intr_pipe_init(pipe, urb->transfer_buffer, urb->transfer_buffer_length);
+ break;
+ case USB_ENDPOINT_TYPE_ISOCHRONOUS:
+ chusb_iso_pipe_init(pipe, urb->transfer_buffer, urb->transfer_buffer_length);
+ break;
+ default:
+ break;
+ }
+ if (urb->timeout > 0) {
+ /* wait until timeout or sem give */
+ ret = usb_osal_sem_take(pipe->waitsem, urb->timeout);
+ if (ret < 0) {
+ goto errout_timeout;
+ }
+
+ g_chusb_hcd.current_pipe_timeout = 0;
+
+ ret = urb->errorcode;
+ }
+ return ret;
+errout_timeout:
+ pipe->waiter = false;
+ usbh_kill_urb(urb);
+ return ret;
+}
+
+int usbh_kill_urb(struct usbh_urb *urb)
+{
+ return 0;
+}
+
+static inline void chusb_pipe_waitup(struct chusb_pipe *pipe, bool callback)
+{
+ struct usbh_urb *urb;
+
+ urb = pipe->urb;
+ pipe->urb = NULL;
+ g_chusb_hcd.main_pipe_using = false;
+
+ if (pipe->waiter) {
+ pipe->waiter = false;
+ usb_osal_sem_give(pipe->waitsem);
+ }
+
+ if (callback == true) {
+ if (urb->complete) {
+ if (urb->errorcode < 0) {
+ urb->complete(urb->arg, urb->errorcode);
+ } else {
+ urb->complete(urb->arg, urb->actual_length);
+ }
+ }
+ }
+}
+
+static int8_t chusb_outpipe_irq_handler(uint8_t res_state)
+{
+ uint16_t current_tx_length = USBHS_HOST->HOST_TX_LEN;
+ struct usbh_urb *urb;
+ urb = (g_chusb_hcd.current_pipe->urb);
+
+ if (g_chusb_hcd.current_pipe->ep_type != USB_ENDPOINT_TYPE_CONTROL) {
+ if ((g_chusb_hcd.current_pipe->ep_addr & 0x80) != 0) {
+ /* Error */
+ USB_LOG_ERR("ep_addr is not out add \r\n");
+ USBHS_HOST->INT_FG = USBHS_TRANSFER_FLAG;
+ return -1;
+ }
+ }
+
+ switch (res_state) {
+ case USB_PID_STALL:
+ urb->errorcode = -EPERM;
+ if (g_chusb_hcd.current_token == USB_PID_SETUP) {
+ USB_LOG_ERR("USB_PID_SETUP STALL \r\n");
+ } else {
+ USB_LOG_ERR("USB_PID_OUT STALL \r\n");
+ }
+ USBHS_HOST->INT_FG = USBHS_TRANSFER_FLAG;
+ return -2;
+ case USB_PID_NAK:
+ urb->errorcode = -EAGAIN;
+ if (g_chusb_hcd.current_pipe->ep_type == USB_ENDPOINT_TYPE_CONTROL) {
+ if (g_chusb_hcd.current_token == USB_PID_SETUP) {
+ /**
+ * Device must ack for setup package
+ */
+ USB_LOG_ERR("Setup NAK \r\n");
+ g_chusb_hcd.current_token = 0;
+ USBHS_HOST->INT_FG = USBHS_TRANSFER_FLAG;
+ return -3;
+ } else {
+ if (g_chusb_hcd.current_pipe_timeout > 0) {
+ USB_LOG_DBG("Control endpoint out nak and retry\r\n");
+ chusb_host_pipe_transfer(g_chusb_hcd.current_pipe, USB_PID_OUT,
+ g_chusb_hcd.current_pipe->buffer, g_chusb_hcd.current_pipe->xferlen);
+ }
+ }
+ } else {
+ urb->errorcode = 0;
+ if (g_chusb_hcd.prv_set_zero == true) {
+ /**
+ * It is unlikely to run here,
+ * because the device can probably receive 0 length byte packets
+ */
+ g_chusb_hcd.prv_set_zero = false;
+ g_chusb_hcd.current_token = 0;
+ urb->actual_length = g_chusb_hcd.current_pipe->xfrd;
+ chusb_pipe_waitup(g_chusb_hcd.current_pipe, true);
+ } else {
+ if (g_chusb_hcd.current_pipe_timeout > 0) {
+ USB_LOG_DBG("Normal endpoint out nak and retry\r\n");
+ chusb_host_pipe_transfer(g_chusb_hcd.current_pipe, USB_PID_OUT,
+ g_chusb_hcd.current_pipe->buffer, g_chusb_hcd.current_pipe->xferlen);
+ } else {
+ g_chusb_hcd.current_token = 0;
+ if (g_chusb_hcd.current_pipe->xfrd > 0) {
+ /**
+ * The device received data but did not receive it completely
+ */
+ urb->actual_length = g_chusb_hcd.current_pipe->xfrd;
+ chusb_pipe_waitup(g_chusb_hcd.current_pipe, true);
+ } else {
+ /**
+ * The device may not be ready, make sure your device can receive data
+ */
+ chusb_pipe_waitup(g_chusb_hcd.current_pipe, false);
+ }
+ }
+ }
+ }
+ break;
+ case USB_PID_ACK:
+ case USB_PID_NYET:
+ /**
+ * The last out or setup package was sent
+ */
+ urb->errorcode = 0;
+
+ if (g_chusb_hcd.current_pipe->ep_type == USB_ENDPOINT_TYPE_CONTROL) {
+ /*!< Ctrol endpoint */
+ if (g_chusb_hcd.current_token == USB_PID_SETUP) {
+ /*!< Setup package send successfully */
+ urb->actual_length += 8;
+ g_chusb_hcd.current_pipe->data_pid ^= 1;
+ if (g_chusb_hcd.ep0_state == USB_EP0_STATE_SETUP) {
+ if (urb->setup->wLength) {
+ if (urb->setup->bmRequestType & 0x80) {
+ g_chusb_hcd.ep0_state = USB_EP0_STATE_IN_DATA;
+ } else {
+ g_chusb_hcd.ep0_state = USB_EP0_STATE_OUT_DATA;
+ }
+ } else {
+ g_chusb_hcd.ep0_state = USB_EP0_STATE_IN_STATUS;
+ }
+ chusb_control_pipe_init(g_chusb_hcd.current_pipe, urb->setup,
+ urb->transfer_buffer, urb->transfer_buffer_length);
+ }
+ } else if (g_chusb_hcd.current_token == USB_PID_OUT) {
+ if (g_chusb_hcd.ep0_state == USB_EP0_STATE_OUT_DATA) {
+ USB_LOG_DBG("ep0 tx_len:%d\r\n", current_tx_length);
+ g_chusb_hcd.current_pipe->data_pid ^= 1;
+ g_chusb_hcd.current_pipe->xfrd += current_tx_length;
+ g_chusb_hcd.current_pipe->buffer += current_tx_length;
+ g_chusb_hcd.current_pipe->xferlen -= current_tx_length;
+ if (g_chusb_hcd.current_pipe->xferlen == 0) {
+ if (current_tx_length == g_chusb_hcd.current_pipe->ep_mps) {
+ /**
+ * Need send 0 length data
+ */
+ chusb_host_pipe_transfer(g_chusb_hcd.current_pipe, USB_PID_OUT, NULL, 0);
+ } else {
+ /*!< Out package send successfully */
+ g_chusb_hcd.ep0_state = USB_EP0_STATE_IN_STATUS;
+ chusb_control_pipe_init(g_chusb_hcd.current_pipe, urb->setup,
+ urb->transfer_buffer, urb->transfer_buffer_length);
+ }
+ } else {
+ /*!< Start send next out package */
+ chusb_host_pipe_transfer(g_chusb_hcd.current_pipe, USB_PID_OUT,
+ g_chusb_hcd.current_pipe->buffer, g_chusb_hcd.current_pipe->xferlen);
+ }
+ } else if (g_chusb_hcd.ep0_state == USB_EP0_STATE_OUT_STATUS) {
+ g_chusb_hcd.ep0_state = USB_EP0_STATE_SETUP;
+ urb->actual_length += g_chusb_hcd.current_pipe->xfrd;
+ USB_LOG_DBG("S-> I-> O-status stage: In:%d\r\n", urb->actual_length - 8);
+ if (g_chusb_hcd.current_pipe->data_pid != 1) {
+ USB_LOG_ERR("S-> I-> O-status stage DATA PID Error\r\n");
+ } else {
+ g_chusb_hcd.current_pipe->data_pid ^= 1;
+ }
+ chusb_pipe_waitup(g_chusb_hcd.current_pipe, true);
+ }
+ }
+ } else {
+ if (g_chusb_hcd.current_token == USB_PID_OUT) {
+ USB_LOG_DBG("ep%d tx_len:%d\r\n", (g_chusb_hcd.current_pipe->ep_addr & 0x0f), current_tx_length);
+ g_chusb_hcd.current_pipe->data_pid ^= 1;
+ g_chusb_hcd.current_pipe->xfrd += current_tx_length;
+ g_chusb_hcd.current_pipe->buffer += current_tx_length;
+ g_chusb_hcd.current_pipe->xferlen -= current_tx_length;
+ if (g_chusb_hcd.current_pipe->xferlen == 0) {
+ if (current_tx_length == g_chusb_hcd.current_pipe->ep_mps) {
+ /**
+ * Need send 0 length data
+ */
+ if (g_chusb_hcd.prv_set_zero == true) {
+ USB_LOG_ERR("g_chusb_hcd.prv_set_zero is always true\r\n");
+ }
+ g_chusb_hcd.prv_set_zero = true;
+ chusb_host_pipe_transfer(g_chusb_hcd.current_pipe, USB_PID_OUT, NULL, 0);
+ } else {
+ /*!< Out package send successfully */
+ g_chusb_hcd.current_token = 0;
+ if (g_chusb_hcd.prv_set_zero == true) {
+ g_chusb_hcd.prv_set_zero = false;
+ }
+ urb->actual_length = g_chusb_hcd.current_pipe->xfrd;
+ chusb_pipe_waitup(g_chusb_hcd.current_pipe, true);
+ }
+ } else {
+ /*!< Start send next out package */
+ chusb_host_pipe_transfer(g_chusb_hcd.current_pipe, USB_PID_OUT,
+ g_chusb_hcd.current_pipe->buffer, g_chusb_hcd.current_pipe->xferlen);
+ }
+ } else {
+ USB_LOG_ERR("Error token is not match \r\n");
+ USBHS_HOST->INT_FG = USBHS_TRANSFER_FLAG;
+ return -4;
+ }
+ }
+ break;
+ case 0:
+ if (g_chusb_hcd.current_pipe->ep_type != USB_ENDPOINT_TYPE_ISOCHRONOUS) {
+ if (g_chusb_hcd.current_token == USB_PID_SETUP) {
+ USB_LOG_ERR("SETUP TIMEOUT \r\n");
+ } else {
+ USB_LOG_ERR("OUT TIMEOUT \r\n");
+ if (g_chusb_hcd.current_pipe->ep_type != USB_ENDPOINT_TYPE_CONTROL) {
+ /**
+ * Reset data pid
+ */
+ g_chusb_hcd.current_pipe->data_pid = 0;
+ }
+ }
+ urb->errorcode = -EIO;
+ USBHS_HOST->INT_FG = USBHS_TRANSFER_FLAG;
+ return -5;
+ } else {
+ /**
+ * No response from isochronous endpoint out
+ */
+ urb->errorcode = 0;
+ chusb_pipe_waitup(g_chusb_hcd.current_pipe, true);
+ }
+ default:
+ break;
+ }
+
+ USBHS_HOST->INT_FG = USBHS_TRANSFER_FLAG;
+ return 0;
+}
+
+static int8_t chusb_inpipe_irq_handler(uint8_t res_state)
+{
+ uint16_t rx_len = 0;
+ struct usbh_urb *urb;
+ urb = (g_chusb_hcd.current_pipe->urb);
+
+ if (g_chusb_hcd.current_pipe->ep_type != USB_ENDPOINT_TYPE_CONTROL) {
+ if ((g_chusb_hcd.current_pipe->ep_addr & 0x80) == 0) {
+ /* Error */
+ USB_LOG_ERR("ep_addr is not in add\r\n");
+ USBHS_HOST->INT_FG = USBHS_TRANSFER_FLAG;
+ return -1;
+ }
+ }
+
+ switch (res_state) {
+ case USB_PID_STALL:
+ USB_LOG_ERR("USB_PID_IN USB_PID_STALL\r\n");
+ g_chusb_hcd.current_token = 0;
+ urb->errorcode = -EPERM;
+ USBHS_HOST->INT_FG = USBHS_TRANSFER_FLAG;
+ return -2;
+ case USB_PID_NAK:
+ urb->errorcode = -EAGAIN;
+ g_chusb_hcd.current_token = 0;
+ if (g_chusb_hcd.current_pipe->ep_type == USB_ENDPOINT_TYPE_CONTROL) {
+ if (g_chusb_hcd.current_pipe_timeout > 0) {
+ chusb_host_pipe_transfer(g_chusb_hcd.current_pipe, USB_PID_IN,
+ g_chusb_hcd.current_pipe->buffer, g_chusb_hcd.current_pipe->xferlen);
+ }
+ } else {
+ urb->errorcode = 0;
+ if (g_chusb_hcd.prv_get_zero == true) {
+ g_chusb_hcd.prv_get_zero = false;
+ g_chusb_hcd.current_token = 0;
+ urb->actual_length = g_chusb_hcd.current_pipe->xfrd;
+ USB_LOG_DBG("Normal endpoint get zero length package\r\n");
+ chusb_pipe_waitup(g_chusb_hcd.current_pipe, true);
+ } else {
+ USB_LOG_DBG("Normal endpoint in nak\r\n");
+ if (g_chusb_hcd.current_pipe->xferlen > 0) {
+ /**
+ * The data has not been transmitted completely
+ */
+ if (g_chusb_hcd.current_pipe->xfrd > 0) {
+ /**
+ * Data was transmitted last time, but this time NAK
+ */
+ if (g_chusb_hcd.current_pipe_timeout > 0) {
+ chusb_host_pipe_transfer(g_chusb_hcd.current_pipe, USB_PID_IN,
+ g_chusb_hcd.current_pipe->buffer, g_chusb_hcd.current_pipe->xferlen);
+ } else {
+ /**
+ * Some data has been transferred
+ */
+ g_chusb_hcd.current_token = 0;
+ urb->actual_length = g_chusb_hcd.current_pipe->xfrd;
+ chusb_pipe_waitup(g_chusb_hcd.current_pipe, true);
+ }
+ } else {
+ g_chusb_hcd.current_token = 0;
+ chusb_pipe_waitup(g_chusb_hcd.current_pipe, false);
+ }
+ } else {
+ USB_LOG_ERR("xferlen == 0//should get zero package\r\n");
+ }
+ }
+ }
+ break;
+ case USB_PID_ACK:
+ urb->errorcode = 0;
+ break;
+ case USB_PID_DATA0:
+ case USB_PID_DATA1:
+ case USB_PID_DATA2:
+ case USB_PID_MDATA:
+ if ((USBHS_HOST->INT_ST) & USBHS_DEV_UIS_TOG_OK) {
+ /*!< Data is OK */
+ rx_len = USBHS_HOST->RX_LEN;
+
+ urb->errorcode = 0;
+
+ g_chusb_hcd.current_pipe->xfrd += rx_len;
+
+ if (g_chusb_hcd.current_pipe->xferlen < rx_len) {
+ g_chusb_hcd.current_pipe->data_pid ^= 1;
+ USB_LOG_ERR("Please provide the correct data length parameter\r\n");
+ USBHS_HOST->INT_FG = USBHS_TRANSFER_FLAG;
+ return -6;
+ }
+
+ g_chusb_hcd.current_pipe->xferlen -= rx_len;
+
+ if (g_chusb_hcd.current_pipe->ep_type == USB_ENDPOINT_TYPE_CONTROL) {
+ /*!< Ctrol endpoint */
+ /**
+ * Status stage
+ *
+ * Setup ---> out data ---> in status stage
+ */
+ if ((g_chusb_hcd.ep0_state == USB_EP0_STATE_IN_STATUS) && (rx_len == 0)) {
+ g_chusb_hcd.ep0_state = USB_EP0_STATE_SETUP;
+ urb->actual_length += rx_len;
+ USB_LOG_DBG("S-> O-> In-status stage: Out:%d\r\n", urb->actual_length - 8);
+
+ if (g_chusb_hcd.current_pipe->data_pid != 1) {
+ USB_LOG_ERR("S-> O-> In-status stage DATA PID Error//PID:%d\r\n", g_chusb_hcd.current_pipe->data_pid);
+ } else {
+ g_chusb_hcd.current_pipe->data_pid ^= 1;
+ }
+
+ chusb_pipe_waitup(g_chusb_hcd.current_pipe, true);
+ USBHS_HOST->INT_FG = USBHS_TRANSFER_FLAG;
+ return 0;
+ }
+
+ /**
+ * Setup ---> in data ---> need generate next out status stage
+ */
+ if (g_chusb_hcd.ep0_state == USB_EP0_STATE_IN_DATA) {
+ USB_LOG_DBG("ep0 rx_len:%d\r\n", rx_len);
+ g_chusb_hcd.current_pipe->data_pid ^= 1;
+ if ((g_chusb_hcd.current_pipe->xfrd) > (urb->setup->wLength)) {
+ /**
+ * Error
+ */
+ USB_LOG_ERR("xfrd > setup->wLength\r\n");
+ USBHS_HOST->INT_FG = USBHS_TRANSFER_FLAG;
+ return -3;
+ }
+
+ if (rx_len == 0 || (rx_len & (g_chusb_hcd.current_pipe->ep_mps - 1))) {
+ /**
+ * Receive a short package, in data has transfer completed
+ */
+ g_chusb_hcd.current_token = 0;
+ g_chusb_hcd.ep0_state = USB_EP0_STATE_OUT_STATUS;
+ /**
+ * generate next out status stage
+ */
+ chusb_control_pipe_init(g_chusb_hcd.current_pipe, urb->setup,
+ urb->transfer_buffer, urb->transfer_buffer_length);
+ } else {
+ /**
+ * Retry in
+ */
+ g_chusb_hcd.current_pipe->buffer += rx_len;
+ chusb_host_pipe_transfer(g_chusb_hcd.current_pipe, USB_PID_IN,
+ g_chusb_hcd.current_pipe->buffer, g_chusb_hcd.current_pipe->xferlen);
+ }
+ }
+ } else {
+ USB_LOG_DBG("ep%d rx_len:%d\r\n", (g_chusb_hcd.current_pipe->ep_addr & 0x0f), rx_len);
+ g_chusb_hcd.current_pipe->data_pid ^= 1;
+ if (rx_len == 0 || (rx_len & (g_chusb_hcd.current_pipe->ep_mps - 1))) {
+ /**
+ * Receive a short package, in data has transfer completed
+ */
+ g_chusb_hcd.current_token = 0;
+ urb->actual_length = g_chusb_hcd.current_pipe->xfrd;
+
+ if (g_chusb_hcd.prv_get_zero == true) {
+ g_chusb_hcd.prv_get_zero = false;
+ }
+
+ chusb_pipe_waitup(g_chusb_hcd.current_pipe, true);
+ } else {
+ /**
+ * Initiate In again
+ */
+ g_chusb_hcd.current_pipe->buffer += rx_len;
+
+ if (g_chusb_hcd.current_pipe->xferlen == 0) {
+ if (g_chusb_hcd.prv_get_zero == true) {
+ USB_LOG_ERR("g_chusb_hcd.prv_get_zero is always true\r\n");
+ g_chusb_hcd.prv_get_zero = false;
+ }
+ g_chusb_hcd.prv_get_zero = true;
+ }
+
+ chusb_host_pipe_transfer(g_chusb_hcd.current_pipe, USB_PID_IN,
+ g_chusb_hcd.current_pipe->buffer, g_chusb_hcd.current_pipe->xferlen);
+ }
+ }
+ } else {
+ /*!< Discard data out of synchronization */
+ USB_LOG_ERR("EP%dData out of sync %d\r\n",
+ (g_chusb_hcd.current_pipe->ep_addr & 0x0f), g_chusb_hcd.current_pipe->data_pid);
+ urb->errorcode = -EIO;
+ USBHS_HOST->INT_FG = USBHS_TRANSFER_FLAG;
+ return -3;
+ }
+ break;
+ default:
+ break;
+ }
+
+ USBHS_HOST->INT_FG = USBHS_TRANSFER_FLAG;
+ return 0;
+}
+
+__attribute__((interrupt("WCH-Interrupt-fast"))) void USBH_IRQHandler(void)
+{
+ INT_PRE_HANDLER();
+ volatile uint8_t intflag = 0;
+ volatile uint8_t res = 0;
+ intflag = USBHS_HOST->INT_FG;
+
+ if (intflag & USBHS_TRANSFER_FLAG) {
+ /*!< Check the equipment response status */
+ res = (USBHS_HOST->INT_ST) & MASK_UIS_H_RES;
+ USBHS_HOST->HOST_EP_PID = 0x00;
+ switch (g_chusb_hcd.current_token) {
+ case USB_PID_SETUP:
+ case USB_PID_OUT:
+ if (chusb_outpipe_irq_handler(res) < 0) {
+ goto pipe_wait;
+ }
+ break;
+ case USB_PID_IN:
+ if (chusb_inpipe_irq_handler(res) < 0) {
+ goto pipe_wait;
+ }
+ break;
+ }
+ } else if (intflag & USBHS_DETECT_FLAG) {
+ if (USBHS_HOST->MIS_ST & USBHS_ATTACH) {
+ USB_LOG_INFO("Dev connect \r\n");
+ g_chusb_hcd.port_csc = 1;
+ g_chusb_hcd.port_pec = 1;
+ g_chusb_hcd.port_pe = 1;
+ usbh_roothub_thread_wakeup(1);
+ } else {
+ USB_LOG_INFO("Dev remove \r\n");
+ if (g_chusb_hcd.main_pipe_using) {
+ g_chusb_hcd.main_pipe_using = false;
+ }
+ g_chusb_hcd.port_csc = 1;
+ g_chusb_hcd.port_pec = 1;
+ g_chusb_hcd.port_pe = 0;
+ for (uint8_t index = 0; index < CONFIG_USBHOST_PIPE_NUM; index++) {
+ for (uint8_t j = 0; j < 2; j++) {
+ struct chusb_pipe *pipe = &g_chusb_hcd.pipe_pool[index][j];
+ struct usbh_urb *urb = pipe->urb;
+ if (pipe->waiter) {
+ pipe->waiter = false;
+ urb->errorcode = -ESHUTDOWN;
+ usb_osal_sem_give(pipe->waitsem);
+ }
+ }
+ }
+ usbh_roothub_thread_wakeup(1);
+ }
+ USBHS_HOST->INT_FG = USBHS_DETECT_FLAG;
+ } else {
+ USB_LOG_INFO("Unkonwn \r\n");
+ USBHS_HOST->INT_FG = intflag;
+ }
+ INT_POST_HANDLER();
+ return;
+pipe_wait:
+ chusb_pipe_waitup(g_chusb_hcd.current_pipe, false);
+ INT_POST_HANDLER();
+}