/* * Copyright (c) 2006-2018, RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * * Change Logs: * Date Author Notes * 2017-10-30 ZYH the first version * 2019-12-19 tyustli port to stm32 series * 2026-01-02 Qwen port to RT-Thread v5.1.0 * 2026-01-03 Qwen fix enumeration: add set_address & fix FIFO timing */ #include "drv_usbh.h" #include "board.h" #include #include #ifdef BSP_USING_USBHOST /* 兼容旧版 RT_DEBUG_LOG 宏(v5.x 已移除) */ #ifndef RT_DEBUG_LOG #ifdef RT_DEBUG_USB #define RT_DEBUG_LOG(mask, fmt) rt_kprintf fmt #else #define RT_DEBUG_LOG(mask, fmt) #endif #endif static HCD_HandleTypeDef stm32_hhcd_fs = {0}; // 显式初始化为 0 static struct rt_completion urb_completion; static volatile rt_bool_t connect_status = RT_FALSE; void OTG_FS_IRQHandler(void) { rt_interrupt_enter(); HAL_HCD_IRQHandler(&stm32_hhcd_fs); rt_interrupt_leave(); } void HAL_HCD_Connect_Callback(HCD_HandleTypeDef *hhcd) { uhcd_t hcd = (uhcd_t)hhcd->pData; if (!connect_status) { connect_status = RT_TRUE; RT_DEBUG_LOG(RT_DEBUG_USB, ("usb connected\n")); rt_usbh_root_hub_connect_handler(hcd, OTG_FS_PORT, RT_FALSE); } } void HAL_HCD_Disconnect_Callback(HCD_HandleTypeDef *hhcd) { uhcd_t hcd = (uhcd_t)hhcd->pData; if (connect_status) { connect_status = RT_FALSE; RT_DEBUG_LOG(RT_DEBUG_USB, ("usb disconnect\n")); rt_usbh_root_hub_disconnect_handler(hcd, OTG_FS_PORT); } } void HAL_HCD_HC_NotifyURBChange_Callback(HCD_HandleTypeDef *hhcd, uint8_t chnum, HCD_URBStateTypeDef urb_state) { rt_completion_done(&urb_completion); } static rt_err_t drv_reset_port(rt_uint8_t port) { RT_DEBUG_LOG(RT_DEBUG_USB, ("reset port\n")); HAL_HCD_ResetPort(&stm32_hhcd_fs); return RT_EOK; } #define USBH_MAX_XFER_SIZE 64 static uint8_t g_usbh_aligned_buffer[USBH_MAX_XFER_SIZE] __attribute__((aligned(4))); static int drv_pipe_xfer(upipe_t pipe, rt_uint8_t token, void *buffer, int nbytes, int timeouts) { int timeout = timeouts; uint8_t hal_pid; void *xfer_buf = buffer; rt_bool_t use_local_buf = RT_FALSE; /* 参数校验 */ if (buffer == RT_NULL || nbytes <= 0) return -RT_EINVAL; if (pipe->pipe_index == 0) { HAL_HCD_HC_Init(&stm32_hhcd_fs, pipe->pipe_index, pipe->ep.bEndpointAddress, pipe->inst->address, USB_OTG_SPEED_FULL, pipe->ep.bmAttributes, pipe->ep.wMaxPacketSize); RT_DEBUG_LOG(RT_DEBUG_USB, ("EP0 channel reinitialized\n")); } if (pipe->pipe_index == 0 && nbytes == 8 && token == 0) /* SETUP stage */ { hal_pid = HC_PID_SETUP; } else if (token == 2) /* IN token */ { /* >>> 关键修复:EP0 的第一次 IN 必须用 DATA1 <<< */ if (pipe->pipe_index == 0) { hal_pid = HC_PID_DATA1; // ← 改为 DATA1! } else { hal_pid = HC_PID_DATA0; } if ((((rt_ubase_t)buffer & 0x3U) != 0) || (nbytes > USBH_MAX_XFER_SIZE)) { xfer_buf = g_usbh_aligned_buffer; use_local_buf = RT_TRUE; } } else /* OUT (including status stage) */ { /* STATUS 阶段也应为 DATA1,但通常用 DATA0 也能工作 */ hal_pid = HC_PID_DATA0; } while (1) { if (!connect_status) { return -RT_ERROR; } rt_completion_init(&urb_completion); /* 提交 USB 事务 */ HAL_HCD_HC_SubmitRequest(&stm32_hhcd_fs, pipe->pipe_index, (pipe->ep.bEndpointAddress & 0x80) >> 7, pipe->ep.bmAttributes, hal_pid, xfer_buf, nbytes, 0); if (hal_pid == HC_PID_SETUP) { rt_thread_mdelay(10); // 至少 1~2ms,让设备处理 SETUP } /* 等待完成 */ rt_err_t wait_result = rt_completion_wait(&urb_completion, timeout); if (wait_result != RT_EOK) { RT_DEBUG_LOG(RT_DEBUG_USB, ("xfer timeout\n")); pipe->status = UPIPE_STATUS_ERROR; if (pipe->callback != RT_NULL) pipe->callback(pipe); return -RT_ETIMEOUT; } rt_thread_mdelay(5); /* 短暂延迟,确保状态更新 */ /* 检查通道状态 */ if (HAL_HCD_HC_GetState(&stm32_hhcd_fs, pipe->pipe_index) == HC_NAK) { RT_DEBUG_LOG(RT_DEBUG_USB, ("nak\n")); if (pipe->ep.bmAttributes == USB_EP_ATTR_INT) { rt_thread_delay((pipe->ep.bInterval * RT_TICK_PER_SECOND / 1000) > 0 ? (pipe->ep.bInterval * RT_TICK_PER_SECOND / 1000) : 1); } /* 重置通道 */ HAL_HCD_HC_Halt(&stm32_hhcd_fs, pipe->pipe_index); HAL_HCD_HC_Init(&stm32_hhcd_fs, pipe->pipe_index, pipe->ep.bEndpointAddress, pipe->inst->address, USB_OTG_SPEED_FULL, pipe->ep.bmAttributes, pipe->ep.wMaxPacketSize); continue; } else if (HAL_HCD_HC_GetState(&stm32_hhcd_fs, pipe->pipe_index) == HC_STALL) { RT_DEBUG_LOG(RT_DEBUG_USB, ("stall\n")); pipe->status = UPIPE_STATUS_STALL; if (pipe->callback != RT_NULL) pipe->callback(pipe); return -RT_EIO; } else if (HAL_HCD_HC_GetURBState(&stm32_hhcd_fs, pipe->pipe_index) == URB_ERROR) { uint32_t hc_state = HAL_HCD_HC_GetState(&stm32_hhcd_fs, pipe->pipe_index); RT_DEBUG_LOG(RT_DEBUG_USB, ("URB_ERROR: HC_STATE=%d\n", hc_state)); pipe->status = UPIPE_STATUS_ERROR; if (pipe->callback != RT_NULL) pipe->callback(pipe); return -RT_ERROR; } else if (HAL_HCD_HC_GetURBState(&stm32_hhcd_fs, pipe->pipe_index) == URB_DONE) { RT_DEBUG_LOG(RT_DEBUG_USB, ("ok\n")); pipe->status = UPIPE_STATUS_OK; if (pipe->callback != RT_NULL) pipe->callback(pipe); size_t xfer_count = HAL_HCD_HC_GetXferCount(&stm32_hhcd_fs, pipe->pipe_index); /* 如果使用了本地对齐缓冲区,拷回数据(仅 IN) */ if (use_local_buf && token == 2) { if (xfer_count > (size_t)nbytes) xfer_count = nbytes; rt_memcpy(buffer, g_usbh_aligned_buffer, xfer_count); } return (int)xfer_count; } /* 其他情况:重试 */ continue; } } static rt_uint16_t pipe_index = 0; static rt_uint8_t drv_get_free_pipe_index(rt_bool_t is_ep0) { rt_uint8_t idx; if (is_ep0) { /* EP0 必须使用 channel 0 */ if (!(pipe_index & (1U << 0))) { pipe_index |= (1U << 0); return 0; } return 0xff; // channel 0 } /* 其他端点从 1~15 分配 */ for (idx = 1; idx < 16; idx++) { if (!(pipe_index & (1U << idx))) { pipe_index |= (1U << idx); return idx; } } return 0xff; } static void drv_free_pipe_index(rt_uint8_t index) { pipe_index &= ~(0x01 << index); } static rt_err_t drv_open_pipe(upipe_t pipe) {rt_bool_t is_ep0 = (pipe->ep.bEndpointAddress == 0); // EP0 地址为 0 pipe->pipe_index = drv_get_free_pipe_index(is_ep0); if (pipe->pipe_index == 0xff) return -RT_ERROR; HAL_HCD_HC_Init(&stm32_hhcd_fs, pipe->pipe_index, pipe->ep.bEndpointAddress, pipe->inst->address, USB_OTG_SPEED_FULL, pipe->ep.bmAttributes, pipe->ep.wMaxPacketSize); /* Set DATA0 PID token */ if (stm32_hhcd_fs.hc[pipe->pipe_index].ep_is_in) { stm32_hhcd_fs.hc[pipe->pipe_index].toggle_in = 0; } else { stm32_hhcd_fs.hc[pipe->pipe_index].toggle_out = 0; } return RT_EOK; } static rt_err_t drv_close_pipe(upipe_t pipe) { HAL_HCD_HC_Halt(&stm32_hhcd_fs, pipe->pipe_index); drv_free_pipe_index(pipe->pipe_index); return RT_EOK; } static rt_err_t drv_set_address(rt_uint8_t dev_addr) { RT_DEBUG_LOG(RT_DEBUG_USB, ("[set_address] address=%d\n", dev_addr)); return RT_EOK; } /* ✅ 修正:uhcd_ops 必须包含 5 个成员(v5.1 要求) */ static struct uhcd_ops _uhcd_ops = { drv_reset_port, drv_pipe_xfer, drv_open_pipe, drv_close_pipe, drv_set_address, }; static rt_err_t stm32_hcd_init(rt_device_t device) { HCD_HandleTypeDef *hhcd = (HCD_HandleTypeDef *)device->user_data; // 确保 HSI48 锁定(推荐) __HAL_RCC_HSI48_ENABLE(); while (__HAL_RCC_GET_FLAG(RCC_FLAG_HSI48RDY) == RESET); hhcd->Instance = USB_OTG_FS; hhcd->Init.Host_channels = 8; hhcd->Init.speed = HCD_SPEED_FULL; hhcd->Init.dma_enable = DISABLE; hhcd->Init.phy_itface = HCD_PHY_EMBEDDED; hhcd->Init.low_power_enable = DISABLE; hhcd->Init.Sof_enable = ENABLE; hhcd->Init.vbus_sensing_enable = DISABLE; if (HAL_HCD_Init(hhcd) != HAL_OK) { rt_kprintf("HAL_HCD_Init failed!\n"); return -RT_ERROR; } // ✅ 关键修正:FIFO 配置必须在 HAL_HCD_Init() 之后! hhcd->Instance->GRXFSIZ = 256; // RX FIFO = 256 words (1024 bytes) hhcd->Instance->HPTXFSIZ = (256 << 16) | 128; HAL_HCD_Start(hhcd); hhcd->Instance->GUSBCFG |= (1UL << 28); #ifdef USBH_USING_CONTROLLABLE_POWER rt_pin_mode(USBH_POWER_PIN, PIN_MODE_OUTPUT); rt_pin_write(USBH_POWER_PIN, PIN_LOW); // 根据硬件确认极性 #endif return RT_EOK; } /* 设备操作函数表 */ static struct rt_device_ops usbh_device_ops = { .init = stm32_hcd_init, .open = RT_NULL, .close = RT_NULL, .read = RT_NULL, .write = RT_NULL, .control = RT_NULL, }; int stm_usbh_register(void) { rt_err_t res = -RT_ERROR; uhcd_t uhcd = (uhcd_t)rt_malloc(sizeof(struct uhcd)); if (uhcd == RT_NULL) { rt_kprintf("uhcd malloc failed\r\n"); return -RT_ENOMEM; } rt_memset((void *)uhcd, 0, sizeof(struct uhcd)); /* 初始化父设备 */ uhcd->parent.type = RT_Device_Class_USBHost; uhcd->parent.ops = &usbh_device_ops; uhcd->parent.user_data = &stm32_hhcd_fs; uhcd->ops = &_uhcd_ops; uhcd->num_ports = OTG_FS_PORT; stm32_hhcd_fs.pData = uhcd; res = rt_device_register(&uhcd->parent, "usbh", RT_DEVICE_FLAG_DEACTIVATE); if (res != RT_EOK) { rt_kprintf("register usb host failed res = %d\r\n", res); rt_free(uhcd); return -RT_ERROR; } rt_usb_host_init("usbh"); return RT_EOK; } INIT_DEVICE_EXPORT(stm_usbh_register); #endif /* BSP_USING_USBHOST */