1+ /*
2+ * Copyright (c) 2025, sakumisu
3+ *
4+ * SPDX-License-Identifier: Apache-2.0
5+ */
6+ #include "usbh_core.h"
7+ #include "usbh_hub.h"
8+ #include "hardware/resets.h"
9+ #include "hardware/irq.h"
10+ #include "hardware/structs/usb.h"
11+
12+ #define usb_hw_set hw_set_alias(usb_hw)
13+ #define usb_hw_clear hw_clear_alias(usb_hw)
14+
15+ struct rp2040_pipe {
16+ uint8_t chidx ;
17+ bool inuse ;
18+ volatile uint32_t * endpoint_control ; /*!< Endpoint control register */
19+ volatile uint32_t * buffer_control ; /*!< Buffer control register */
20+ uint8_t * data_buffer ; /*!< Buffer pointer in usb dpram */
21+ uint32_t buffer_size ; /*!< Buffer size */
22+ usb_osal_sem_t waitsem ;
23+ struct usbh_urb * urb ;
24+ };
25+
26+ struct rp2040_hcd {
27+ volatile bool port_csc ;
28+ volatile bool port_pec ;
29+ volatile bool port_pe ;
30+ struct rp2040_pipe pipe_pool [1 + CONFIG_USBHOST_PIPE_NUM ];
31+ } g_rp2040_hcd [CONFIG_USBHOST_MAX_BUS ];
32+
33+ void rp2040_usbh_irq (void );
34+
35+ /**
36+ * @brief Take a buffer pointer located in the USB RAM and return as an offset of the RAM.
37+ *
38+ * @param buf
39+ * @return uint32_t
40+ */
41+ static inline uint32_t usb_buffer_offset (volatile uint8_t * buf )
42+ {
43+ return (uint32_t )buf ^ (uint32_t )usbh_dpram ;
44+ }
45+
46+ static inline uint8_t usbh_get_port_speed (void )
47+ {
48+ return (usb_hw -> sie_status & USB_SIE_STATUS_SPEED_BITS ) >> USB_SIE_STATUS_SPEED_LSB ;
49+ }
50+
51+ int usb_hc_init (struct usbh_bus * bus )
52+ {
53+ uint8_t * next_buffer_ptr ;
54+
55+ memset (& g_rp2040_hcd [bus -> hcd .hcd_id ], 0 , sizeof (struct rp2040_hcd ));
56+
57+ for (uint8_t i = 0 ; i <= CONFIG_USBHOST_PIPE_NUM ; i ++ ) {
58+ g_rp2040_hcd [bus -> hcd .hcd_id ].pipe_pool [i ].waitsem = usb_osal_sem_create (0 );
59+ }
60+
61+ g_rp2040_hcd [bus -> hcd .hcd_id ].pipe_pool [0 ].endpoint_control = & usbh_dpram -> epx_ctrl ;
62+ g_rp2040_hcd [bus -> hcd .hcd_id ].pipe_pool [0 ].buffer_control = & usbh_dpram -> epx_buf_ctrl ;
63+ g_rp2040_hcd [bus -> hcd .hcd_id ].pipe_pool [0 ].data_buffer = & usbh_dpram -> epx_data [0 ];
64+
65+ next_buffer_ptr = & usb_dpram -> epx_data [64 * 2 ];
66+
67+ for (uint8_t i = 1 ; i <= CONFIG_USBHOST_PIPE_NUM ; i ++ ) {
68+ g_rp2040_hcd [bus -> hcd .hcd_id ].pipe_pool [i ].chidx = i ;
69+ g_rp2040_hcd [bus -> hcd .hcd_id ].pipe_pool [i ].endpoint_control = & usbh_dpram -> int_ep_ctrl [i ].ctrl ;
70+ g_rp2040_hcd [bus -> hcd .hcd_id ].pipe_pool [i ].buffer_control = & usbh_dpram -> int_ep_buffer_ctrl [i ].ctrl ;
71+ g_rp2040_hcd [bus -> hcd .hcd_id ].pipe_pool [i ].data_buffer = next_buffer_ptr ;
72+ g_rp2040_hcd [bus -> hcd .hcd_id ].pipe_pool [i ].buffer_size = (64 * 2 );
73+ next_buffer_ptr += (64 * 2 );
74+ }
75+
76+ // Reset usb controller
77+ reset_unreset_block_num_wait_blocking (RESET_USBCTRL );
78+
79+ // Remove shared irq if it was previously added so as not to fill up shared irq slots
80+ irq_remove_handler (USBCTRL_IRQ , rp2040_usbh_irq );
81+
82+ irq_add_shared_handler (USBCTRL_IRQ , rp2040_usbh_irq , PICO_SHARED_IRQ_HANDLER_HIGHEST_ORDER_PRIORITY );
83+
84+ /*!< Clear any previous state just in case */
85+ memset (usb_hw , 0 , sizeof (* usb_hw ));
86+ memset (usbh_dpram , 0 , sizeof (* usbh_dpram ));
87+
88+ /*!< Mux the controller to the onboard usb phy */
89+ usb_hw -> muxing = USB_USB_MUXING_TO_PHY_BITS | USB_USB_MUXING_SOFTCON_BITS ;
90+
91+ // Force VBUS detect so the device thinks it is plugged into a host
92+ usb_hw -> pwr = USB_USB_PWR_VBUS_DETECT_BITS | USB_USB_PWR_VBUS_DETECT_OVERRIDE_EN_BITS ;
93+
94+ // Enable the USB controller in device mode.
95+ usb_hw -> main_ctrl = USB_MAIN_CTRL_CONTROLLER_EN_BITS | USB_MAIN_CTRL_HOST_NDEVICE_BITS ;
96+
97+ usb_hw -> sie_ctrl = USB_SIE_CTRL_SOF_EN_BITS |
98+ USB_SIE_CTRL_KEEP_ALIVE_EN_BITS |
99+ USB_SIE_CTRL_PULLDOWN_EN_BITS |
100+ USB_SIE_CTRL_EP0_INT_1BUF_BITS ;
101+
102+ // Enable USB interrupt at processor
103+ irq_set_enabled (USBCTRL_IRQ , true);
104+
105+ usb_hw -> inte = USB_INTE_BUFF_STATUS_BITS |
106+ USB_INTE_HOST_CONN_DIS_BITS |
107+ USB_INTE_HOST_RESUME_BITS |
108+ USB_INTE_STALL_BITS |
109+ USB_INTE_TRANS_COMPLETE_BITS |
110+ USB_INTE_ERROR_RX_TIMEOUT_BITS |
111+ USB_INTE_ERROR_DATA_SEQ_BITS ;
112+ return 0 ;
113+ }
114+
115+ int usb_hc_deinit (struct usbh_bus * bus )
116+ {
117+ // Enable USB interrupt at processor
118+ irq_set_enabled (USBCTRL_IRQ , false);
119+
120+ // Remove shared irq if it was previously added so as not to fill up shared irq slots
121+ irq_remove_handler (USBCTRL_IRQ , rp2040_usbh_irq );
122+
123+ return 0 ;
124+ }
125+
126+ uint16_t usbh_get_frame_number (struct usbh_bus * bus )
127+ {
128+ return 0 ;
129+ }
130+
131+ int usbh_roothub_control (struct usbh_bus * bus , struct usb_setup_packet * setup , uint8_t * buf )
132+ {
133+ uint8_t nports ;
134+ uint8_t port ;
135+ uint32_t status ;
136+
137+ nports = CONFIG_USBHOST_MAX_RHPORTS ;
138+ port = setup -> wIndex ;
139+ if (setup -> bmRequestType & USB_REQUEST_RECIPIENT_DEVICE ) {
140+ switch (setup -> bRequest ) {
141+ case HUB_REQUEST_CLEAR_FEATURE :
142+ switch (setup -> wValue ) {
143+ case HUB_FEATURE_HUB_C_LOCALPOWER :
144+ break ;
145+ case HUB_FEATURE_HUB_C_OVERCURRENT :
146+ break ;
147+ default :
148+ return - USB_ERR_INVAL ;
149+ }
150+ break ;
151+ case HUB_REQUEST_SET_FEATURE :
152+ switch (setup -> wValue ) {
153+ case HUB_FEATURE_HUB_C_LOCALPOWER :
154+ break ;
155+ case HUB_FEATURE_HUB_C_OVERCURRENT :
156+ break ;
157+ default :
158+ return - USB_ERR_INVAL ;
159+ }
160+ break ;
161+ case HUB_REQUEST_GET_DESCRIPTOR :
162+ break ;
163+ case HUB_REQUEST_GET_STATUS :
164+ memset (buf , 0 , 4 );
165+ break ;
166+ default :
167+ break ;
168+ }
169+ } else if (setup -> bmRequestType & USB_REQUEST_RECIPIENT_OTHER ) {
170+ switch (setup -> bRequest ) {
171+ case HUB_REQUEST_CLEAR_FEATURE :
172+ if (!port || port > nports ) {
173+ return - USB_ERR_INVAL ;
174+ }
175+
176+ switch (setup -> wValue ) {
177+ case HUB_PORT_FEATURE_ENABLE :
178+ break ;
179+ case HUB_PORT_FEATURE_SUSPEND :
180+ case HUB_PORT_FEATURE_C_SUSPEND :
181+ break ;
182+ case HUB_PORT_FEATURE_POWER :
183+ break ;
184+ case HUB_PORT_FEATURE_C_CONNECTION :
185+ g_rp2040_hcd [bus -> hcd .hcd_id ].port_csc = 0 ;
186+ break ;
187+ case HUB_PORT_FEATURE_C_ENABLE :
188+ g_rp2040_hcd [bus -> hcd .hcd_id ].port_pec = 0 ;
189+ break ;
190+ case HUB_PORT_FEATURE_C_OVER_CURREN :
191+ break ;
192+ case HUB_PORT_FEATURE_C_RESET :
193+ break ;
194+ default :
195+ return - USB_ERR_INVAL ;
196+ }
197+ break ;
198+ case HUB_REQUEST_SET_FEATURE :
199+ if (!port || port > nports ) {
200+ return - USB_ERR_INVAL ;
201+ }
202+
203+ switch (setup -> wValue ) {
204+ case HUB_PORT_FEATURE_SUSPEND :
205+ break ;
206+ case HUB_PORT_FEATURE_POWER :
207+ break ;
208+ case HUB_PORT_FEATURE_RESET :
209+ break ;
210+
211+ default :
212+ return - USB_ERR_INVAL ;
213+ }
214+ break ;
215+ case HUB_REQUEST_GET_STATUS :
216+ if (!port || port > nports ) {
217+ return - USB_ERR_INVAL ;
218+ }
219+
220+ status = 0 ;
221+ if (g_rp2040_hcd [bus -> hcd .hcd_id ].port_csc ) {
222+ status |= (1 << HUB_PORT_FEATURE_C_CONNECTION );
223+ }
224+ if (g_rp2040_hcd [bus -> hcd .hcd_id ].port_pec ) {
225+ status |= (1 << HUB_PORT_FEATURE_C_ENABLE );
226+ }
227+
228+ if (g_rp2040_hcd [bus -> hcd .hcd_id ].port_pe ) {
229+ status |= (1 << HUB_PORT_FEATURE_CONNECTION );
230+ status |= (1 << HUB_PORT_FEATURE_ENABLE );
231+ if (usbh_get_port_speed () == USB_SPEED_LOW ) {
232+ status |= (1 << HUB_PORT_FEATURE_LOWSPEED );
233+ }
234+ }
235+
236+ status |= (1 << HUB_PORT_FEATURE_POWER );
237+ memcpy (buf , & status , 4 );
238+ break ;
239+ default :
240+ break ;
241+ }
242+ }
243+ return 0 ;
244+ }
245+
246+ int usbh_submit_urb (struct usbh_urb * urb )
247+ {
248+ return - USB_ERR_NOTSUPP ;
249+ }
250+
251+ int usbh_kill_urb (struct usbh_urb * urb )
252+ {
253+ return - USB_ERR_NOTSUPP ;
254+ }
255+
256+ void USBH_IRQHandler (uint8_t busid )
257+ {
258+ uint32_t status = usb_hw -> ints ;
259+ uint32_t handled = 0 ;
260+ struct usbh_bus * bus ;
261+
262+ bus = & g_usbhost_bus [busid ];
263+
264+ if (status & USB_INTS_HOST_CONN_DIS_BITS ) {
265+ handled |= USB_INTS_HOST_CONN_DIS_BITS ;
266+ usb_hw_clear -> sie_status = USB_SIE_STATUS_SPEED_BITS ;
267+ if (usbh_get_port_speed ()) {
268+ g_rp2040_hcd [bus -> hcd .hcd_id ].port_csc = 1 ;
269+ g_rp2040_hcd [bus -> hcd .hcd_id ].port_pec = 1 ;
270+ g_rp2040_hcd [bus -> hcd .hcd_id ].port_pe = 1 ;
271+ bus -> hcd .roothub .int_buffer [0 ] = (1 << 1 );
272+ usbh_hub_thread_wakeup (& bus -> hcd .roothub );
273+ } else {
274+ g_rp2040_hcd [bus -> hcd .hcd_id ].port_csc = 1 ;
275+ g_rp2040_hcd [bus -> hcd .hcd_id ].port_pec = 1 ;
276+ g_rp2040_hcd [bus -> hcd .hcd_id ].port_pe = 0 ;
277+ bus -> hcd .roothub .int_buffer [0 ] = (1 << 1 );
278+ usbh_hub_thread_wakeup (& bus -> hcd .roothub );
279+ }
280+ }
281+
282+ if (status & USB_INTS_STALL_BITS ) {
283+ handled |= USB_INTS_STALL_BITS ;
284+ usb_hw_clear -> sie_status = USB_SIE_STATUS_STALL_REC_BITS ;
285+ }
286+
287+ if (status & USB_INTS_BUFF_STATUS_BITS ) {
288+ handled |= USB_INTS_BUFF_STATUS_BITS ;
289+ }
290+
291+ if (status & USB_INTS_TRANS_COMPLETE_BITS ) {
292+ handled |= USB_INTS_TRANS_COMPLETE_BITS ;
293+ usb_hw_clear -> sie_status = USB_SIE_STATUS_TRANS_COMPLETE_BITS ;
294+ }
295+
296+ if (status & USB_INTS_ERROR_RX_TIMEOUT_BITS ) {
297+ handled |= USB_INTS_ERROR_RX_TIMEOUT_BITS ;
298+ usb_hw_clear -> sie_status = USB_SIE_STATUS_RX_TIMEOUT_BITS ;
299+ }
300+
301+ if (status & USB_INTS_ERROR_DATA_SEQ_BITS ) {
302+ usb_hw_clear -> sie_status = USB_SIE_STATUS_DATA_SEQ_ERROR_BITS ;
303+ }
304+
305+ if (status ^ handled ) {
306+ USB_LOG_ERR ("Unhandled IRQ 0x%x\n" , (uint )(status ^ handled ));
307+ }
308+ }
309+
310+ void rp2040_usbh_irq (void )
311+ {
312+ USBH_IRQHandler (0 );
313+ }
0 commit comments