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