/* * Copyright (c) 2006-2023, 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 */ #include "drv_usbh.h" #include "board.h" #define LOG_TAG "drv.usb.host" #define DBG_LVL DBG_INFO #include static HCD_HandleTypeDef stm32_hhcd_fs; 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; LOG_D("usb connected"); 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; LOG_D("usb disconnnect"); 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) { LOG_D("reset port"); HAL_HCD_ResetPort(&stm32_hhcd_fs); return RT_EOK; } static int drv_pipe_xfer(upipe_t pipe, rt_uint8_t token, void *buffer, int nbytes, int timeouts) { int timeout = timeouts; while (1) { if (!connect_status) { return -1; } rt_completion_init(&urb_completion); HAL_HCD_HC_SubmitRequest(&stm32_hhcd_fs, pipe->pipe_index, (pipe->ep.bEndpointAddress & 0x80) >> 7, pipe->ep.bmAttributes, token, buffer, nbytes, 0); rt_completion_wait(&urb_completion, timeout); rt_thread_mdelay(1); if (HAL_HCD_HC_GetState(&stm32_hhcd_fs, pipe->pipe_index) == HC_NAK) { LOG_D("nak"); 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) { LOG_D("stall"); pipe->status = UPIPE_STATUS_STALL; if (pipe->callback != RT_NULL) { pipe->callback(pipe); } return -1; } else if (HAL_HCD_HC_GetState(&stm32_hhcd_fs, pipe->pipe_index) == URB_ERROR) { LOG_D("error"); pipe->status = UPIPE_STATUS_ERROR; if (pipe->callback != RT_NULL) { pipe->callback(pipe); } return -1; } else if(URB_DONE == HAL_HCD_HC_GetURBState(&stm32_hhcd_fs, pipe->pipe_index)) { LOG_D("ok"); pipe->status = UPIPE_STATUS_OK; if (pipe->callback != RT_NULL) { pipe->callback(pipe); } size_t size = HAL_HCD_HC_GetXferCount(&stm32_hhcd_fs, pipe->pipe_index); if (pipe->ep.bEndpointAddress & 0x80) { return size; } else if (pipe->ep.bEndpointAddress & 0x00) { return size; } return nbytes; } continue; } } static rt_uint16_t pipe_index = 0; static rt_uint8_t drv_get_free_pipe_index(void) { rt_uint8_t idx; for (idx = 1; idx < 16; idx++) { if (!(pipe_index & (0x01 << idx))) { pipe_index |= (0x01 << 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) { pipe->pipe_index = drv_get_free_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); /* 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 struct uhcd_ops _uhcd_ops = { drv_reset_port, drv_pipe_xfer, drv_open_pipe, drv_close_pipe, }; static rt_err_t stm32_hcd_init(rt_device_t device) { HAL_StatusTypeDef state; HCD_HandleTypeDef *hhcd = (HCD_HandleTypeDef *)device->user_data; 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.Sof_enable = DISABLE; state = HAL_HCD_Init(hhcd); if (state != HAL_OK) { return -RT_ERROR; } HAL_HCD_Start(hhcd); #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; } 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_ERROR; } rt_memset((void *)uhcd, 0, sizeof(struct uhcd)); uhcd->parent.type = RT_Device_Class_USBHost; uhcd->parent.init = stm32_hcd_init; 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); return -RT_ERROR; } rt_usb_host_init("usbh"); return RT_EOK; } INIT_DEVICE_EXPORT(stm_usbh_register);