1 /*
2  * Copyright (c) 2006-2024, RT-Thread Development Team
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
6  * Change Logs:
7  * Date           Author       Notes
8  * 2024-4-30      IceBear003   the first version adapted from CherryUSB
9  */
10 
11 #include "board.h"
12 #include "drv_usbh.h"
13 
14 #ifdef BSP_USING_USBH
15 
16 #define LOG_TAG "drv.usbh"
17 #include "drv_log.h"
18 
19 static struct uhcd uhcd;
20 
21 static struct rt_completion urb_completion;
22 
23 __attribute__((aligned(4))) int8_t usb_rx_buf[ MAX_PACKET_SIZE ];
24 __attribute__((aligned(4))) int8_t usb_tx_buf[ MAX_PACKET_SIZE ];
25 
26 USBOTGH_FS_TypeDef *USBFSH = USBOTG_H_FS;
27 
usbh_reset_port(rt_uint8_t port)28 rt_err_t usbh_reset_port(rt_uint8_t port)
29 {
30     //Disable interrupt
31     USBFSH->INT_EN &= (~USBFS_UIE_DETECT);
32     USBFSH->HOST_CTRL &= ~USBFS_UH_SOF_EN;
33 
34     //Set address
35     USBFSH->DEV_ADDR = (USBFS_UDA_GP_BIT & USBFSH->DEV_ADDR) | (0x00 & USBFS_USB_ADDR_MASK);
36 
37     //Close port
38     USBFSH->HOST_CTRL &= ~USBFS_UH_PORT_EN;
39 
40     //Reset
41     USBFSH->HOST_CTRL |= USBFS_UH_BUS_RESET;
42     rt_thread_mdelay(30);
43     USBFSH->HOST_CTRL &= ~USBFS_UH_BUS_RESET;
44     rt_thread_mdelay(20);
45 
46     //Set speed
47     if ((USBFSH->HOST_CTRL & USBFS_UH_PORT_EN) == 0) { //Set speed only when port is closed
48         if(USBFSH->MIS_ST & USBFS_UMS_DM_LEVEL)        //RB_UMS_DM_LEVEL: 1/Low 0/Full
49         {   //Low speed
50             USBFSH->BASE_CTRL |= USBFS_UC_LOW_SPEED;
51             USBFSH->HOST_CTRL |= USBFS_UH_LOW_SPEED;
52             USBFSH->HOST_SETUP |= USBFS_UH_PRE_PID_EN;
53         }
54         else
55         {   //Full speed
56             USBFSH->BASE_CTRL &= ~USBFS_UC_LOW_SPEED;
57             USBFSH->HOST_CTRL &= ~USBFS_UH_LOW_SPEED;
58             USBFSH->HOST_SETUP &= ~USBFS_UH_PRE_PID_EN;
59         }
60     }
61 
62     //Enable port
63     USBFSH->HOST_CTRL |= USBFS_UH_PORT_EN;
64     USBFSH->HOST_SETUP |= USBFS_UH_SOF_EN;
65 
66     //Enable interrupt
67     USBFSH->INT_EN |= USBFS_UIE_DETECT;
68 
69     if(USBFSH->INT_FG & USBFS_UIF_DETECT)
70         if(USBFSH->MIS_ST & USBFS_UMS_DEV_ATTACH)
71             USBFSH->INT_FG = USBFS_UIF_DETECT;
72 
73     return RT_EOK;
74 }
75 
usbh_pipe_xfer(upipe_t pipe,rt_uint8_t token,void * buffer,int nbytes,int timeouts)76 int usbh_pipe_xfer(upipe_t pipe, rt_uint8_t token, void *buffer, int nbytes, int timeouts)
77 {
78     rt_uint8_t usb_pid;
79     rt_uint8_t *tog = (rt_uint8_t *)pipe->user_data;
80 
81     USBFSH->DEV_ADDR = (USBFS_UDA_GP_BIT & USBFSH->DEV_ADDR) | (pipe->pipe_index & USBFS_USB_ADDR_MASK);
82 
83     if(pipe->ep.bEndpointAddress & USB_DIR_IN)
84     {
85         usb_pid = USB_PID_IN;
86         USBFSH->HOST_TX_LEN = 0x00;
87     }
88     else
89     {
90         usb_pid = (token == USBH_PID_SETUP) ? USB_PID_SETUP : USB_PID_OUT;
91         rt_memcpy(usb_tx_buf, buffer, nbytes);
92         USBFSH->HOST_TX_LEN = nbytes;
93     }
94 
95     switch(usb_pid)
96     {
97     case USB_PID_IN:
98         if (nbytes == 0) *tog = USB_PID_DATA1;
99         USBFSH->HOST_RX_CTRL = (*tog == USB_PID_DATA1) ? USBFS_UH_R_TOG : 0x00;
100         break;
101     case USB_PID_OUT:
102         if (nbytes == 0) *tog = USB_PID_DATA1;
103         USBFSH->HOST_TX_CTRL = (*tog == USB_PID_DATA1) ? USBFS_UH_T_TOG : 0x00;
104         break;
105     case USB_PID_SETUP:
106         *(rt_uint8_t *)pipe->inst->pipe_ep0_out->user_data = USB_PID_DATA0;
107         *(rt_uint8_t *)pipe->inst->pipe_ep0_in->user_data = USB_PID_DATA1;
108         USBFSH->HOST_TX_CTRL = (*tog == USB_PID_DATA1) ? USBFS_UH_T_TOG : 0x00;
109         break;
110     }
111 
112     rt_uint16_t try = 3;
113     if ((pipe->ep.bmAttributes & USB_EP_ATTR_TYPE_MASK) == USB_EP_ATTR_CONTROL)
114         try = 1000;
115 
116     while(try--)
117     {
118         USBFSH->HOST_EP_PID = (usb_pid << 4) | (pipe->ep.bEndpointAddress & 0x0F);
119         if (rt_completion_wait(&urb_completion, timeouts) != RT_EOK)
120             continue;
121 
122         if (USBFSH->INT_ST & USBHS_UIS_TOG_OK)
123             *tog = (*tog == USB_PID_DATA0) ? USB_PID_DATA1 : USB_PID_DATA0;
124 
125         switch(USBFSH->INT_ST & USBHS_UIS_H_RES_MASK)
126         {
127         case USB_PID_DATA0:
128         case USB_PID_DATA1:
129             pipe->status = UPIPE_STATUS_OK;
130             if (pipe->callback != RT_NULL)
131                 pipe->callback(pipe);
132             if (usb_pid == USB_PID_IN)
133             {
134                 rt_memcpy(buffer, usb_rx_buf, USBFSH->RX_LEN);
135                 return USBFSH->RX_LEN;
136             }
137             return nbytes;
138 
139         case USB_PID_ACK:
140             pipe->status = UPIPE_STATUS_OK;
141             if (pipe->callback != RT_NULL)
142                 pipe->callback(pipe);
143             return nbytes;
144         case USB_PID_NAK:
145             if (pipe->ep.bmAttributes == USB_EP_ATTR_INT)
146                 rt_thread_delay((pipe->ep.bInterval * RT_TICK_PER_SECOND / 1000) > 0 ? (pipe->ep.bInterval * RT_TICK_PER_SECOND / 1000) : 1);
147             rt_thread_mdelay(1);
148             continue;
149 
150         case USB_PID_STALL:
151             pipe->status = UPIPE_STATUS_STALL;
152             if (pipe->callback != RT_NULL)
153                 pipe->callback(pipe);
154             return 0;
155 
156         default:
157             break;
158         }
159     }
160 
161     pipe->status = UPIPE_STATUS_ERROR;
162     if (pipe->callback != RT_NULL)
163         pipe->callback(pipe);
164 
165     return -RT_ERROR;
166 }
167 
usbh_open_pipe(upipe_t pipe)168 rt_err_t usbh_open_pipe(upipe_t pipe)
169 {
170     pipe->pipe_index = pipe->inst->address & USBFS_USB_ADDR_MASK;
171     pipe->user_data = rt_malloc(sizeof(rt_uint8_t));
172     *(rt_uint8_t *)pipe->user_data = (pipe->ep.bEndpointAddress & USB_DIR_IN) ? USB_PID_DATA0 : USB_PID_DATA0;
173     return RT_EOK;
174 }
175 
usbh_close_pipe(upipe_t pipe)176 rt_err_t usbh_close_pipe(upipe_t pipe)
177 {
178     rt_free(pipe->user_data);
179     return RT_EOK;
180 }
181 
hcd_init(rt_device_t dev)182 rt_err_t hcd_init(rt_device_t dev)
183 {
184     //Force                  reset handler        clear FIFO & interrupt flags
185     USBFSH->BASE_CTRL = USBFS_UC_RESET_SIE | USBFS_UC_CLR_ALL;
186     rt_thread_mdelay(20);
187     USBFSH->BASE_CTRL = 0;     //Set SIE to 0 manually
188 
189     USBFSH->BASE_CTRL = USBFS_UC_HOST_MODE;
190     USBFSH->HOST_CTRL = 0;
191     USBFSH->DEV_ADDR = 0;
192     USBFSH->HOST_EP_MOD = USBFS_UH_EP_TX_EN | USBFS_UH_EP_RX_EN; //SETUP/OUT/IN
193 
194     USBFSH->HOST_RX_DMA = (uint32_t)usb_rx_buf;
195     USBFSH->HOST_TX_DMA = (uint32_t)usb_tx_buf;
196 
197     USBFSH->HOST_RX_CTRL = 0;
198     USBFSH->HOST_TX_CTRL = 0;
199     USBFSH->BASE_CTRL = USBFS_UC_HOST_MODE | USBFS_UC_INT_BUSY | USBFS_UC_DMA_EN;
200 
201     USBFSH->INT_FG = 0xFF;
202     USBFSH->INT_EN = USBFS_UIE_TRANSFER | USBFS_UIE_DETECT;
203 
204     if( SystemCoreClock == 144000000 )
205         RCC_OTGFSCLKConfig( RCC_OTGFSCLKSource_PLLCLK_Div3 );
206     else if( SystemCoreClock == 96000000 )
207         RCC_OTGFSCLKConfig( RCC_OTGFSCLKSource_PLLCLK_Div2 );
208     else if( SystemCoreClock == 48000000 )
209         RCC_OTGFSCLKConfig( RCC_OTGFSCLKSource_PLLCLK_Div1 );
210     RCC_AHBPeriphClockCmd( RCC_AHBPeriph_OTG_FS, ENABLE );
211 
212     NVIC_EnableIRQ(OTG_FS_IRQn);
213 
214     rt_completion_init(&urb_completion);
215 
216     return RT_EOK;
217 }
218 
219 static struct uhcd_ops uhcd_ops =
220 {
221     .reset_port = usbh_reset_port,
222     .pipe_xfer = usbh_pipe_xfer,
223     .open_pipe = usbh_open_pipe,
224     .close_pipe = usbh_close_pipe,
225 };
226 
227 void OTG_HS_IRQHandler() __attribute__((interrupt("WCH-Interrupt-fast")));
OTG_HS_IRQHandler()228 void OTG_HS_IRQHandler()
229 {
230     rt_interrupt_enter();
231 
232     if (USBFSH->INT_FG & USBFS_UIF_TRANSFER)
233     {
234         USBFSH->HOST_EP_PID = 0x00;
235         USBFSH->INT_FG = USBFS_UIF_TRANSFER;
236         rt_completion_done(&urb_completion);                            //本应在此处理的中断在传输函数里进行枚举
237         rt_kprintf("USB: Transfer finished\n");
238     }
239     else if (USBFSH->INT_FG & USBFS_UIF_DETECT)
240     {
241         if (USBFSH->MIS_ST & USBFS_UMS_DEV_ATTACH)
242         {
243             rt_usbh_root_hub_connect_handler(&uhcd, 1, RT_FALSE);
244             rt_kprintf("USB: Connect\n");
245         }
246         else
247         {
248             USBFSH->HOST_SETUP &= ~USBFS_UH_SOF_EN;
249             USBFSH->HOST_CTRL &= ~USBFS_UH_PORT_EN;
250 
251             rt_usbh_root_hub_disconnect_handler(&uhcd, 1);
252             rt_kprintf("USB: Disconnect\n");
253         }
254         USBFSH->INT_FG = USBFS_UIF_DETECT;
255     }
256     else
257     {
258         rt_kprintf("USB: Unknown\n");
259     }
260 
261     rt_interrupt_leave();
262 }
263 
rt_hw_usbh_init()264 int rt_hw_usbh_init()
265 {
266     rt_err_t res = -RT_ERROR;
267 
268     rt_memset((void *)&uhcd, 0, sizeof(struct uhcd));
269     uhcd.parent.type = RT_Device_Class_USBHost;
270     uhcd.parent.init = hcd_init;
271     uhcd.ops = &uhcd_ops;
272     uhcd.num_ports = 1;
273 
274     res = rt_device_register(&uhcd.parent, "usbh", RT_DEVICE_FLAG_DEACTIVATE);
275 
276     if (res != RT_EOK)
277     {
278         rt_kprintf("register usb host failed res = %d\r\n", res);
279         return -RT_ERROR;
280     }
281 
282     rt_usb_host_init();
283 
284     return RT_EOK;
285 }
286 #endif //BSP_USING_USBH