1 /*
2 * SPDX-FileCopyrightText: 2020-2024 Espressif Systems (Shanghai) CO LTD
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #pragma once
8
9 #include "soc/soc_caps.h"
10 /*
11 This header is shared across all targets. Resolve to an empty header for targets
12 that don't support USB OTG.
13 */
14 #if SOC_USB_OTG_SUPPORTED
15 #include <stdint.h>
16 #include <stdbool.h>
17 #include "hal/usb_dwc_ll.h"
18 #include "hal/usb_dwc_types.h"
19 #include "hal/assert.h"
20 #endif // SOC_USB_OTG_SUPPORTED
21
22 #ifdef __cplusplus
23 extern "C" {
24 #endif
25
26 #if SOC_USB_OTG_SUPPORTED
27
28 // ------------------------------------------------ Macros and Types ---------------------------------------------------
29
30 // ----------------------- Configs -------------------------
31
32 /**
33 * @brief Possible FIFO biases
34 */
35 typedef enum {
36 USB_HAL_FIFO_BIAS_DEFAULT, /**< Default (balanced) FIFO sizes */
37 USB_HAL_FIFO_BIAS_RX, /**< Bigger RX FIFO for IN transfers */
38 USB_HAL_FIFO_BIAS_PTX, /**< Bigger periodic TX FIFO for ISOC OUT transfers */
39 } usb_hal_fifo_bias_t;
40
41 /**
42 * @brief MPS limits based on FIFO configuration
43 *
44 * In bytes
45 *
46 * The resulting values depend on
47 * 1. FIFO total size (chip specific)
48 * 2. Set FIFO bias
49 */
50 typedef struct {
51 unsigned int in_mps; /**< Maximum packet size of IN packet */
52 unsigned int non_periodic_out_mps; /**< Maximum packet size of BULK and CTRL OUT packets */
53 unsigned int periodic_out_mps; /**< Maximum packet size of INTR and ISOC OUT packets */
54 } usb_hal_fifo_mps_limits_t;
55
56 /**
57 * @brief FIFO size configuration structure
58 */
59 typedef struct {
60 uint32_t rx_fifo_lines; /**< Size of the RX FIFO in terms the number of FIFO lines */
61 uint32_t nptx_fifo_lines; /**< Size of the Non-periodic FIFO in terms the number of FIFO lines */
62 uint32_t ptx_fifo_lines; /**< Size of the Periodic FIFO in terms the number of FIFO lines */
63 } usb_dwc_hal_fifo_config_t;
64
65 // --------------------- HAL Events ------------------------
66
67 /**
68 * @brief Host port HAL events
69 */
70 typedef enum {
71 USB_DWC_HAL_PORT_EVENT_NONE, /**< No event occurred, or could not decode interrupt */
72 USB_DWC_HAL_PORT_EVENT_CHAN, /**< A channel event has occurred. Call the the channel event handler instead */
73 USB_DWC_HAL_PORT_EVENT_CONN, /**< The host port has detected a connection */
74 USB_DWC_HAL_PORT_EVENT_DISCONN, /**< The host port has been disconnected */
75 USB_DWC_HAL_PORT_EVENT_ENABLED, /**< The host port has been enabled (i.e., connected to a device that has been reset. Started sending SOFs) */
76 USB_DWC_HAL_PORT_EVENT_DISABLED, /**< The host port has been disabled (no more SOFs). Could be due to disable/reset request, or a port error (e.g. port babble condition. See 11.8.1 of USB2.0 spec) */
77 USB_DWC_HAL_PORT_EVENT_OVRCUR, /**< The host port has encountered an overcurrent condition */
78 USB_DWC_HAL_PORT_EVENT_OVRCUR_CLR, /**< The host port has been cleared of the overcurrent condition */
79 } usb_dwc_hal_port_event_t;
80
81 /**
82 * @brief Channel events
83 */
84 typedef enum {
85 USB_DWC_HAL_CHAN_EVENT_CPLT, /**< The channel has completed execution of a transfer descriptor that had the USB_DWC_HAL_XFER_DESC_FLAG_HOC flag set. Channel is now halted */
86 USB_DWC_HAL_CHAN_EVENT_ERROR, /**< The channel has encountered an error. Channel is now halted. */
87 USB_DWC_HAL_CHAN_EVENT_HALT_REQ, /**< The channel has been successfully halted as requested */
88 USB_DWC_HAL_CHAN_EVENT_NONE, /**< No event (interrupt ran for internal processing) */
89 } usb_dwc_hal_chan_event_t;
90
91 // --------------------- HAL Errors ------------------------
92
93 /**
94 * @brief Channel errors
95 */
96 typedef enum {
97 USB_DWC_HAL_CHAN_ERROR_XCS_XACT = 0, /**< Excessive (three consecutive) transaction errors (e.g., no response, bad CRC etc */
98 USB_DWC_HAL_CHAN_ERROR_BNA, /**< Buffer Not Available error (i.e., An inactive transfer descriptor was fetched by the channel) */
99 USB_DWC_HAL_CHAN_ERROR_PKT_BBL, /**< Packet babbler error (packet exceeded MPS) */
100 USB_DWC_HAL_CHAN_ERROR_STALL, /**< STALL response received */
101 } usb_dwc_hal_chan_error_t;
102
103 // ------------- Transfer Descriptor Related ---------------
104
105 /**
106 * @brief Flags used to describe the type of transfer descriptor to fill
107 */
108 #define USB_DWC_HAL_XFER_DESC_FLAG_IN 0x01 /**< Indicates this transfer descriptor is of the IN direction */
109 #define USB_DWC_HAL_XFER_DESC_FLAG_SETUP 0x02 /**< Indicates this transfer descriptor is an OUT setup */
110 #define USB_DWC_HAL_XFER_DESC_FLAG_HOC 0x04 /**< Indicates that the channel will be halted after this transfer descriptor completes */
111
112 /**
113 * @brief Status value of a transfer descriptor
114 *
115 * A transfer descriptor's status remains unexecuted until the entire transfer descriptor completes (either successfully
116 * or an error). Therefore, if a channel halt is requested before a transfer descriptor completes, the transfer
117 * descriptor remains unexecuted.
118 */
119 #define USB_DWC_HAL_XFER_DESC_STS_SUCCESS USB_DWC_LL_QTD_STATUS_SUCCESS
120 #define USB_DWC_HAL_XFER_DESC_STS_PKTERR USB_DWC_LL_QTD_STATUS_PKTERR
121 #define USB_DWC_HAL_XFER_DESC_STS_BUFFER_ERR USB_DWC_LL_QTD_STATUS_BUFFER
122 #define USB_DWC_HAL_XFER_DESC_STS_NOT_EXECUTED USB_DWC_LL_QTD_STATUS_NOT_EXECUTED
123
124 // -------------------- Object Types -----------------------
125
126 /**
127 * @brief Endpoint characteristics structure
128 */
129 typedef struct {
130 union {
131 struct {
132 usb_dwc_xfer_type_t type: 2; /**< The type of endpoint */
133 uint32_t bEndpointAddress: 8; /**< Endpoint address (containing endpoint number and direction) */
134 uint32_t mps: 11; /**< Maximum Packet Size */
135 uint32_t dev_addr: 8; /**< Device Address */
136 uint32_t ls_via_fs_hub: 1; /**< The endpoint is on a LS device that is routed through an FS hub.
137 Setting this bit will lead to the addition of the PREamble packet */
138 uint32_t reserved2: 2;
139 };
140 uint32_t val;
141 };
142 struct {
143 unsigned int interval; /**< The interval of the endpoint in frames (FS) or microframes (HS) */
144 uint32_t offset; /**< Offset of this channel in the periodic scheduler */
145 bool is_hs; /**< This endpoint is HighSpeed. Needed for Periodic Frame List (HAL layer) scheduling */
146 } periodic; /**< Characteristic for periodic (interrupt/isochronous) endpoints only */
147 } usb_dwc_hal_ep_char_t;
148
149 /**
150 * @brief Channel object
151 */
152 typedef struct {
153 //Channel control, status, and information
154 union {
155 struct {
156 uint32_t active: 1; /**< Debugging bit to indicate whether channel is enabled */
157 uint32_t halt_requested: 1; /**< A halt has been requested */
158 uint32_t reserved: 2;
159 uint32_t chan_idx: 4; /**< The index number of the channel */
160 uint32_t reserved24: 24;
161 };
162 uint32_t val;
163 } flags; /**< Flags regarding channel's status and information */
164 usb_dwc_host_chan_regs_t *regs; /**< Pointer to the channel's register set */
165 usb_dwc_hal_chan_error_t error; /**< The last error that occurred on the channel */
166 usb_dwc_xfer_type_t type; /**< The transfer type of the channel */
167 void *chan_ctx; /**< Context variable for the owner of the channel */
168 } usb_dwc_hal_chan_t;
169
170 /**
171 * @brief HAL context structure
172 */
173 typedef struct {
174 //Context
175 usb_dwc_dev_t *dev; /**< Pointer to base address of DWC_OTG registers */
176 //Host Port related
177 uint32_t *periodic_frame_list; /**< Pointer to scheduling frame list */
178 usb_hal_frame_list_len_t frame_list_len; /**< Length of the periodic scheduling frame list */
179 //FIFO related
180 usb_dwc_hal_fifo_config_t fifo_config; /**< FIFO sizes configuration */
181 union {
182 struct {
183 uint32_t dbnc_lock_enabled: 1; /**< Debounce lock enabled */
184 uint32_t fifo_sizes_set: 1; /**< Whether the FIFO sizes have been set or not */
185 uint32_t periodic_sched_enabled: 1; /**< Periodic scheduling (for interrupt and isochronous transfers) is enabled */
186 uint32_t reserved: 5;
187 uint32_t reserved24: 24;
188 };
189 uint32_t val;
190 } flags;
191 //Channel related
192 struct {
193 int num_allocd; /**< Number of channels currently allocated */
194 uint32_t chan_pend_intrs_msk; /**< Bit mask of channels with pending interrupts */
195 usb_dwc_hal_chan_t *hdls[OTG_NUM_HOST_CHAN]; /**< Handles of each channel. Set to NULL if channel has not been allocated */
196 } channels;
197 } usb_dwc_hal_context_t;
198
199 // -------------------------------------------------- Core (Global) ----------------------------------------------------
200
201 /**
202 * @brief Initialize the HAL context and check if DWC_OTG is alive
203 *
204 * Entry:
205 * - The peripheral must have been reset and clock un-gated
206 * - The USB PHY (internal or external) and associated GPIOs must already be configured
207 * - GPIO pins configured
208 * - Interrupt allocated but DISABLED (in case of an unknown interrupt state)
209 * Exit:
210 * - Checks to see if DWC_OTG is alive, and if HW version/config is correct
211 * - HAl context initialized
212 * - Sets default values to some global and OTG registers (GAHBCFG and GUSBCFG)
213 * - Umask global interrupt signal
214 * - Put DWC_OTG into host mode. Require 25ms delay before this takes effect.
215 * - State -> USB_DWC_HAL_PORT_STATE_OTG
216 * - Interrupts cleared. Users can now enable their ISR
217 *
218 * @param[inout] hal Context of the HAL layer
219 */
220 void usb_dwc_hal_init(usb_dwc_hal_context_t *hal);
221
222 /**
223 * @brief Deinitialize the HAL context
224 *
225 * Entry:
226 * - All channels must be properly disabled, and any pending events handled
227 * Exit:
228 * - DWC_OTG global interrupt disabled
229 * - HAL context deinitialized
230 *
231 * @param hal Context of the HAL layer
232 */
233 void usb_dwc_hal_deinit(usb_dwc_hal_context_t *hal);
234
235 /**
236 * @brief Issue a soft reset to the controller
237 *
238 * This should be called when the host port encounters an error event or has been disconnected. Before calling this,
239 * users are responsible for safely freeing all channels as a soft reset will wipe all host port and channel registers.
240 * This function will result in the host port being put back into same state as after calling usb_dwc_hal_init().
241 *
242 * @note This has nothing to do with a USB bus reset. It simply resets the peripheral
243 *
244 * @param hal Context of the HAL layer
245 */
246 void usb_dwc_hal_core_soft_reset(usb_dwc_hal_context_t *hal);
247
248 /**
249 * @brief Set FIFO bias
250 *
251 * This function will set the sizes of each of the FIFOs (RX FIFO, Non-periodic TX FIFO, Periodic TX FIFO) and must be
252 * called at least once before allocating the channel. Based on the type of endpoints (and the endpoints' MPS), there
253 * may be situations where this function may need to be called again to resize the FIFOs. If resizing FIFOs dynamically,
254 * it is the user's responsibility to ensure there are no active channels when this function is called.
255 *
256 * @note After a port reset, the FIFO size registers will reset to their default values, so this function must be called
257 * again post reset.
258 *
259 * @param[in] hal Context of the HAL layer
260 * @param[in] fifo_bias FIFO bias configuration
261 */
262 void usb_dwc_hal_set_fifo_bias(usb_dwc_hal_context_t *hal, const usb_hal_fifo_bias_t fifo_bias);
263
264 /**
265 * @brief Get MPS limits
266 *
267 * @param[in] hal Context of the HAL layer
268 * @param[out] mps_limits MPS limits
269 */
270 void usb_dwc_hal_get_mps_limits(usb_dwc_hal_context_t *hal, usb_hal_fifo_mps_limits_t *mps_limits);
271
272 // ---------------------------------------------------- Host Port ------------------------------------------------------
273
274 // ------------------ Host Port Control --------------------
275
276 /**
277 * @brief Initialize the host port
278 *
279 * - Will enable the host port's interrupts allowing port and channel events to occur
280 *
281 * @param hal Context of the HAL layer
282 */
usb_dwc_hal_port_init(usb_dwc_hal_context_t * hal)283 static inline void usb_dwc_hal_port_init(usb_dwc_hal_context_t *hal)
284 {
285 //Configure Host related interrupts
286 usb_dwc_ll_haintmsk_dis_chan_intr(hal->dev, 0xFFFFFFFF); //Disable interrupts for all channels
287 usb_dwc_ll_gintmsk_en_intrs(hal->dev, USB_DWC_LL_INTR_CORE_PRTINT | USB_DWC_LL_INTR_CORE_HCHINT);
288 }
289
290 /**
291 * @brief Deinitialize the host port
292 *
293 * - Will disable the host port's interrupts preventing further port aand channel events from occurring
294 *
295 * @param hal Context of the HAL layer
296 */
usb_dwc_hal_port_deinit(usb_dwc_hal_context_t * hal)297 static inline void usb_dwc_hal_port_deinit(usb_dwc_hal_context_t *hal)
298 {
299 //Disable Host port and channel interrupts
300 usb_dwc_ll_gintmsk_dis_intrs(hal->dev, USB_DWC_LL_INTR_CORE_PRTINT | USB_DWC_LL_INTR_CORE_HCHINT);
301 }
302
303 /**
304 * @brief Toggle the host port's power
305 *
306 * @param hal Context of the HAL layer
307 * @param power_on Whether to power ON or OFF the port
308 */
usb_dwc_hal_port_toggle_power(usb_dwc_hal_context_t * hal,bool power_on)309 static inline void usb_dwc_hal_port_toggle_power(usb_dwc_hal_context_t *hal, bool power_on)
310 {
311 if (power_on) {
312 usb_dwc_ll_hprt_en_pwr(hal->dev);
313 } else {
314 usb_dwc_ll_hprt_dis_pwr(hal->dev);
315 }
316 }
317
318 /**
319 * @brief Toggle reset signal on the bus
320 *
321 * The reset signal should be held for at least 10ms
322 * Entry:
323 * - Host port detects a device connection or Host port is already enabled
324 * Exit:
325 * - On release of the reset signal, a USB_DWC_HAL_PORT_EVENT_ENABLED will be generated
326 *
327 * @note If the host port is already enabled, then issuing a reset will cause it be disabled and generate a
328 * USB_DWC_HAL_PORT_EVENT_DISABLED event. The host port will not be enabled until the reset signal is released (thus
329 * generating the USB_DWC_HAL_PORT_EVENT_ENABLED event)
330 *
331 * @param hal Context of the HAL layer
332 * @param enable Enable/disable reset signal
333 */
usb_dwc_hal_port_toggle_reset(usb_dwc_hal_context_t * hal,bool enable)334 static inline void usb_dwc_hal_port_toggle_reset(usb_dwc_hal_context_t *hal, bool enable)
335 {
336 usb_dwc_ll_hprt_set_port_reset(hal->dev, enable);
337 }
338
339 /**
340 * @brief Enable the host port
341 *
342 * Entry:
343 * - Host port enabled event triggered following a reset
344 * Exit:
345 * - Host port enabled to operate in scatter/gather DMA mode
346 * - DMA fifo sizes configured
347 *
348 * @param hal Context of the HAL layer
349 */
350 void usb_dwc_hal_port_enable(usb_dwc_hal_context_t *hal);
351
352 /**
353 * @brief Disable the host port
354 *
355 * Exit:
356 * - Host port disabled event triggered
357 *
358 * @param hal Context of the HAL layer
359 */
usb_dwc_hal_port_disable(usb_dwc_hal_context_t * hal)360 static inline void usb_dwc_hal_port_disable(usb_dwc_hal_context_t *hal)
361 {
362 usb_dwc_ll_hprt_port_dis(hal->dev);
363 }
364
365 /**
366 * @brief Suspend the host port
367 *
368 * @param hal Context of the HAL layers
369 */
usb_dwc_hal_port_suspend(usb_dwc_hal_context_t * hal)370 static inline void usb_dwc_hal_port_suspend(usb_dwc_hal_context_t *hal)
371 {
372 usb_dwc_ll_hprt_set_port_suspend(hal->dev);
373 }
374
375 /**
376 * @brief Toggle resume signal on the bus
377 *
378 * Hosts should hold the resume signal for at least 20ms
379 *
380 * @note If a remote wakeup event occurs, the resume signal is driven and cleared automatically.
381 *
382 * @param hal Context of the HAL layer
383 * @param enable Enable/disable resume signal
384 */
usb_dwc_hal_port_toggle_resume(usb_dwc_hal_context_t * hal,bool enable)385 static inline void usb_dwc_hal_port_toggle_resume(usb_dwc_hal_context_t *hal, bool enable)
386 {
387 if (enable) {
388 usb_dwc_ll_hprt_set_port_resume(hal->dev);
389 } else {
390 usb_dwc_ll_hprt_clr_port_resume(hal->dev);
391 }
392 }
393
394 /**
395 * @brief Check whether the resume signal is being driven
396 *
397 * If a remote wakeup event occurs, the core will automatically drive and clear the resume signal for the required
398 * amount of time. Call this function to check whether the resume signal has completed.
399 *
400 * @param hal Context of the HAL layer
401 * @return true Resume signal is still being driven
402 * @return false Resume signal is no longer driven
403 */
usb_dwc_hal_port_check_resume(usb_dwc_hal_context_t * hal)404 static inline bool usb_dwc_hal_port_check_resume(usb_dwc_hal_context_t *hal)
405 {
406 return usb_dwc_ll_hprt_get_port_resume(hal->dev);
407 }
408
409 // ---------------- Host Port Scheduling -------------------
410
411 /**
412 * @brief Sets the periodic scheduling frame list
413 *
414 * @note This function must be called before attempting configuring any channels to be period via
415 * usb_dwc_hal_chan_set_ep_char()
416 *
417 * @param hal Context of the HAL layer
418 * @param frame_list Base address of the frame list
419 * @param frame_list_len Number of entries in the frame list (can only be 8, 16, 32, 64)
420 */
usb_dwc_hal_port_set_frame_list(usb_dwc_hal_context_t * hal,uint32_t * frame_list,usb_hal_frame_list_len_t len)421 static inline void usb_dwc_hal_port_set_frame_list(usb_dwc_hal_context_t *hal, uint32_t *frame_list, usb_hal_frame_list_len_t len)
422 {
423 //Clear and save frame list
424 hal->periodic_frame_list = frame_list;
425 hal->frame_list_len = len;
426 }
427
428 /**
429 * @brief Enable periodic scheduling
430 *
431 * @note The periodic frame list must be set via usb_dwc_hal_port_set_frame_list() should be set before calling this
432 * function
433 * @note This function must be called before activating any periodic channels
434 *
435 * @param hal Context of the HAL layer
436 */
usb_dwc_hal_port_periodic_enable(usb_dwc_hal_context_t * hal)437 static inline void usb_dwc_hal_port_periodic_enable(usb_dwc_hal_context_t *hal)
438 {
439 HAL_ASSERT(hal->periodic_frame_list != NULL);
440 usb_dwc_ll_hflbaddr_set_base_addr(hal->dev, (uint32_t)hal->periodic_frame_list);
441 usb_dwc_ll_hcfg_set_num_frame_list_entries(hal->dev, hal->frame_list_len);
442 usb_dwc_ll_hcfg_en_perio_sched(hal->dev);
443 hal->flags.periodic_sched_enabled = 1;
444 }
445
446 /**
447 * @brief Disable periodic scheduling
448 *
449 * Disabling periodic scheduling will save a bit of DMA bandwidth (as the controller will no longer fetch the schedule
450 * from the frame list).
451 *
452 * @note Before disabling periodic scheduling, it is the user's responsibility to ensure that all periodic channels have
453 * halted safely.
454 *
455 * @param hal Context of the HAL layer
456 */
usb_dwc_hal_port_periodic_disable(usb_dwc_hal_context_t * hal)457 static inline void usb_dwc_hal_port_periodic_disable(usb_dwc_hal_context_t *hal)
458 {
459 HAL_ASSERT(hal->flags.periodic_sched_enabled);
460 usb_dwc_ll_hcfg_dis_perio_sched(hal->dev);
461 hal->flags.periodic_sched_enabled = 0;
462 }
463
usb_dwc_hal_port_get_cur_frame_num(usb_dwc_hal_context_t * hal)464 static inline uint32_t usb_dwc_hal_port_get_cur_frame_num(usb_dwc_hal_context_t *hal)
465 {
466 return usb_dwc_ll_hfnum_get_frame_num(hal->dev);
467 }
468
469 // --------------- Host Port Status/State ------------------
470
471 /**
472 * @brief Check if a device is currently connected to the host port
473 *
474 * This function is intended to be called after one of the following events followed by an adequate debounce delay
475 * - USB_DWC_HAL_PORT_EVENT_CONN
476 * - USB_DWC_HAL_PORT_EVENT_DISCONN
477 *
478 * @note No other connection/disconnection event will occur again until the debounce lock is disabled via
479 * usb_dwc_hal_disable_debounce_lock()
480 *
481 * @param hal Context of the HAL layer
482 * @return true A device is connected to the host port
483 * @return false A device is not connected to the host port
484 */
usb_dwc_hal_port_check_if_connected(usb_dwc_hal_context_t * hal)485 static inline bool usb_dwc_hal_port_check_if_connected(usb_dwc_hal_context_t *hal)
486 {
487 return usb_dwc_ll_hprt_get_conn_status(hal->dev);
488 }
489
490 /**
491 * @brief Check the speed of the device connected to the host port
492 *
493 * @note This function should only be called after confirming that a device is connected to the host port
494 *
495 * @param hal Context of the HAL layer
496 * @return usb_dwc_speed_t Speed of the connected device
497 */
usb_dwc_hal_port_get_conn_speed(usb_dwc_hal_context_t * hal)498 static inline usb_dwc_speed_t usb_dwc_hal_port_get_conn_speed(usb_dwc_hal_context_t *hal)
499 {
500 return usb_dwc_ll_hprt_get_speed(hal->dev);
501 }
502
503 /**
504 * @brief Disable the debounce lock
505 *
506 * This function must be called after calling usb_dwc_hal_port_check_if_connected() and will allow connection/disconnection
507 * events to occur again. Any pending connection or disconnection interrupts are cleared.
508 *
509 * @param hal Context of the HAL layer
510 */
usb_dwc_hal_disable_debounce_lock(usb_dwc_hal_context_t * hal)511 static inline void usb_dwc_hal_disable_debounce_lock(usb_dwc_hal_context_t *hal)
512 {
513 hal->flags.dbnc_lock_enabled = 0;
514 //Clear Connection and disconnection interrupt in case it triggered again
515 usb_dwc_ll_gintsts_clear_intrs(hal->dev, USB_DWC_LL_INTR_CORE_DISCONNINT);
516 usb_dwc_ll_hprt_intr_clear(hal->dev, USB_DWC_LL_INTR_HPRT_PRTCONNDET);
517 //Re-enable the hprt (connection) and disconnection interrupts
518 usb_dwc_ll_gintmsk_en_intrs(hal->dev, USB_DWC_LL_INTR_CORE_PRTINT | USB_DWC_LL_INTR_CORE_DISCONNINT);
519 }
520
521 // ----------------------------------------------------- Channel -------------------------------------------------------
522
523 // ----------------- Channel Allocation --------------------
524
525 /**
526 * @brief Allocate a channel
527 *
528 * @param[in] hal Context of the HAL layer
529 * @param[inout] chan_obj Empty channel object
530 * @param[in] chan_ctx Context variable for the allocator of the channel
531 * @return true Channel successfully allocated
532 * @return false Failed to allocate channel
533 */
534 bool usb_dwc_hal_chan_alloc(usb_dwc_hal_context_t *hal, usb_dwc_hal_chan_t *chan_obj, void *chan_ctx);
535
536 /**
537 * @brief Free a channel
538 *
539 * @param[in] hal Context of the HAL layer
540 * @param[in] chan_obj Channel object
541 */
542 void usb_dwc_hal_chan_free(usb_dwc_hal_context_t *hal, usb_dwc_hal_chan_t *chan_obj);
543
544 // ---------------- Channel Configuration ------------------
545
546 /**
547 * @brief Get the context variable of the channel
548 *
549 * @param[in] chan_obj Channel object
550 * @return void* The context variable of the channel
551 */
usb_dwc_hal_chan_get_context(usb_dwc_hal_chan_t * chan_obj)552 static inline void *usb_dwc_hal_chan_get_context(usb_dwc_hal_chan_t *chan_obj)
553 {
554 return chan_obj->chan_ctx;
555 }
556
557 /**
558 * @brief Set the endpoint information for a particular channel
559 *
560 * This should be called when a channel switches target from one EP to another
561 *
562 * @note the channel must be in the disabled state in order to change its EP
563 * information
564 *
565 * @param hal Context of the HAL layer
566 * @param chan_obj Channel object
567 * @param ep_char Endpoint characteristics
568 */
569 void usb_dwc_hal_chan_set_ep_char(usb_dwc_hal_context_t *hal, usb_dwc_hal_chan_t *chan_obj, usb_dwc_hal_ep_char_t *ep_char);
570
571 /**
572 * @brief Set the direction of the channel
573 *
574 * This is a convenience function to flip the direction of a channel without
575 * needing to reconfigure all of the channel's EP info. This is used primarily
576 * for control transfers.
577 *
578 * @note This function should only be called when the channel is halted
579 *
580 * @param chan_obj Channel object
581 * @param is_in Whether the direction is IN
582 */
usb_dwc_hal_chan_set_dir(usb_dwc_hal_chan_t * chan_obj,bool is_in)583 static inline void usb_dwc_hal_chan_set_dir(usb_dwc_hal_chan_t *chan_obj, bool is_in)
584 {
585 //Cannot change direction whilst channel is still active or in error
586 HAL_ASSERT(!chan_obj->flags.active);
587 usb_dwc_ll_hcchar_set_dir(chan_obj->regs, is_in);
588 }
589
590 /**
591 * @brief Set the next Packet ID of the channel (e.g., DATA0/DATA1)
592 *
593 * This should be called when a channel switches target from one EP to another
594 * or when change stages for a control transfer
595 *
596 * @note The channel should only be called when the channel is in the
597 * halted state.
598 *
599 * @param chan_obj Channel object
600 * @param pid PID of the next DATA packet (DATA0 or DATA1)
601 */
usb_dwc_hal_chan_set_pid(usb_dwc_hal_chan_t * chan_obj,int pid)602 static inline void usb_dwc_hal_chan_set_pid(usb_dwc_hal_chan_t *chan_obj, int pid)
603 {
604 //Cannot change pid whilst channel is still active or in error
605 HAL_ASSERT(!chan_obj->flags.active);
606 //Update channel object and set the register
607 usb_dwc_ll_hctsiz_set_pid(chan_obj->regs, pid);
608 }
609
610 /**
611 * @brief Get the next PID of a channel
612 *
613 * Returns the next PID (DATA0 or DATA1) of the channel. This function should be
614 * used when the next PID of a pipe needs to be saved (e.g., when switching pipes
615 * on a channel)
616 *
617 * @param chan_obj Channel object
618 * @return uint32_t Starting PID of the next transfer (DATA0 or DATA1)
619 */
usb_dwc_hal_chan_get_pid(usb_dwc_hal_chan_t * chan_obj)620 static inline uint32_t usb_dwc_hal_chan_get_pid(usb_dwc_hal_chan_t *chan_obj)
621 {
622 HAL_ASSERT(!chan_obj->flags.active);
623 return usb_dwc_ll_hctsiz_get_pid(chan_obj->regs);
624 }
625
626 // ------------------- Channel Control ---------------------
627
628 /**
629 * @brief Activate a channel
630 *
631 * Activating a channel will cause the channel to start executing transfer descriptors.
632 *
633 * @note This function should only be called on channels that were previously halted
634 * @note An event will be generated when the channel is halted
635 *
636 * @param chan_obj Channel object
637 * @param xfer_desc_list A filled transfer descriptor list
638 * @param desc_list_len Transfer descriptor list length
639 * @param start_idx Index of the starting transfer descriptor in the list
640 */
641 void usb_dwc_hal_chan_activate(usb_dwc_hal_chan_t *chan_obj, void *xfer_desc_list, int desc_list_len, int start_idx);
642
643 /**
644 * @brief Get the index of the current transfer descriptor
645 *
646 * @param chan_obj Channel object
647 * @return int Descriptor index
648 */
usb_dwc_hal_chan_get_qtd_idx(usb_dwc_hal_chan_t * chan_obj)649 static inline int usb_dwc_hal_chan_get_qtd_idx(usb_dwc_hal_chan_t *chan_obj)
650 {
651 return usb_dwc_ll_hcdam_get_cur_qtd_idx(chan_obj->regs);
652 }
653
654 /**
655 * @brief Request to halt a channel
656 *
657 * This function should be called in order to halt a channel. If the channel is already halted, this function will
658 * return true. If the channel is still active, this function will return false and users must wait for the
659 * USB_DWC_HAL_CHAN_EVENT_HALT_REQ event before treating the channel as halted.
660 *
661 * @note When a transfer is in progress (i.e., the channel is active) and a halt is requested, the channel will halt
662 * after the next USB packet is completed. If the transfer has more pending packets, the transfer will just be
663 * marked as USB_DWC_HAL_XFER_DESC_STS_NOT_EXECUTED.
664 *
665 * @param chan_obj Channel object
666 * @return true The channel is already halted
667 * @return false The halt was requested, wait for USB_DWC_HAL_CHAN_EVENT_HALT_REQ
668 */
669 bool usb_dwc_hal_chan_request_halt(usb_dwc_hal_chan_t *chan_obj);
670
671 /**
672 * @brief Indicate that a channel is halted after a port error
673 *
674 * When a port error occurs (e.g., disconnect, overcurrent):
675 * - Any previously active channels will remain active (i.e., they will not receive a channel interrupt)
676 * - Attempting to disable them using usb_dwc_hal_chan_request_halt() will NOT generate an interrupt for ISOC channels
677 * (probably something to do with the periodic scheduling)
678 *
679 * However, the channel's enable bit can be left as 1 since after a port error, a soft reset will be done anyways.
680 * This function simply updates the channels internal state variable to indicate it is halted (thus allowing it to be
681 * freed).
682 *
683 * @param chan_obj Channel object
684 */
usb_dwc_hal_chan_mark_halted(usb_dwc_hal_chan_t * chan_obj)685 static inline void usb_dwc_hal_chan_mark_halted(usb_dwc_hal_chan_t *chan_obj)
686 {
687 chan_obj->flags.active = 0;
688 }
689
690 /**
691 * @brief Get a channel's error
692 *
693 * @param chan_obj Channel object
694 * @return usb_dwc_hal_chan_error_t The type of error the channel has encountered
695 */
usb_dwc_hal_chan_get_error(usb_dwc_hal_chan_t * chan_obj)696 static inline usb_dwc_hal_chan_error_t usb_dwc_hal_chan_get_error(usb_dwc_hal_chan_t *chan_obj)
697 {
698 return chan_obj->error;
699 }
700
701 // -------------------------------------------- Transfer Descriptor List -----------------------------------------------
702
703 /**
704 * @brief Fill a single entry in a transfer descriptor list
705 *
706 * - Depending on the transfer type, a single transfer descriptor may corresponds
707 * - A stage of a transfer (for control transfers)
708 * - A frame of a transfer interval (for interrupt and isoc)
709 * - An entire transfer (for bulk transfers)
710 * - Check the various USB_DWC_HAL_XFER_DESC_FLAG_ flags for filling a specific type of descriptor
711 * - For IN transfer entries, set the USB_DWC_HAL_XFER_DESC_FLAG_IN. The transfer size must also be an integer multiple of
712 * the endpoint's MPS
713 *
714 * @note Critical section is not required for this function
715 *
716 * @param desc_list Transfer descriptor list
717 * @param desc_idx Transfer descriptor index
718 * @param xfer_data_buff Transfer data buffer
719 * @param xfer_len Transfer length
720 * @param flags Transfer flags
721 */
usb_dwc_hal_xfer_desc_fill(void * desc_list,uint32_t desc_idx,uint8_t * xfer_data_buff,int xfer_len,uint32_t flags)722 static inline void usb_dwc_hal_xfer_desc_fill(void *desc_list, uint32_t desc_idx, uint8_t *xfer_data_buff, int xfer_len, uint32_t flags)
723 {
724 usb_dwc_ll_dma_qtd_t *qtd_list = (usb_dwc_ll_dma_qtd_t *)desc_list;
725 if (flags & USB_DWC_HAL_XFER_DESC_FLAG_IN) {
726 usb_dwc_ll_qtd_set_in(&qtd_list[desc_idx],
727 xfer_data_buff, xfer_len,
728 flags & USB_DWC_HAL_XFER_DESC_FLAG_HOC);
729 } else {
730 usb_dwc_ll_qtd_set_out(&qtd_list[desc_idx],
731 xfer_data_buff,
732 xfer_len,
733 flags & USB_DWC_HAL_XFER_DESC_FLAG_HOC,
734 flags & USB_DWC_HAL_XFER_DESC_FLAG_SETUP);
735 }
736 }
737
738 /**
739 * @brief Clear a transfer descriptor (sets all its fields to NULL)
740 *
741 * @param desc_list Transfer descriptor list
742 * @param desc_idx Transfer descriptor index
743 */
usb_dwc_hal_xfer_desc_clear(void * desc_list,uint32_t desc_idx)744 static inline void usb_dwc_hal_xfer_desc_clear(void *desc_list, uint32_t desc_idx)
745 {
746 usb_dwc_ll_dma_qtd_t *qtd_list = (usb_dwc_ll_dma_qtd_t *)desc_list;
747 usb_dwc_ll_qtd_set_null(&qtd_list[desc_idx]);
748 }
749
750 /**
751 * @brief Parse a transfer decriptor's results
752 *
753 * @param desc_list Transfer descriptor list
754 * @param desc_idx Transfer descriptor index
755 * @param[out] xfer_rem_len Remaining length of the transfer in bytes
756 * @param[out] xfer_status Status of the transfer
757 *
758 * @note Critical section is not required for this function
759 */
usb_dwc_hal_xfer_desc_parse(void * desc_list,uint32_t desc_idx,int * xfer_rem_len,int * xfer_status)760 static inline void usb_dwc_hal_xfer_desc_parse(void *desc_list, uint32_t desc_idx, int *xfer_rem_len, int *xfer_status)
761 {
762 usb_dwc_ll_dma_qtd_t *qtd_list = (usb_dwc_ll_dma_qtd_t *)desc_list;
763 usb_dwc_ll_qtd_get_status(&qtd_list[desc_idx], xfer_rem_len, xfer_status);
764 //Clear the QTD to prevent it from being read again
765 usb_dwc_ll_qtd_set_null(&qtd_list[desc_idx]);
766 }
767
768 // ------------------------------------------------- Event Handling ----------------------------------------------------
769
770 /**
771 * @brief Decode global and host port interrupts
772 *
773 * - Reads and clears global and host port interrupt registers
774 * - Decodes the interrupt bits to determine what host port event occurred
775 *
776 * @note This should be the first interrupt decode function to be run
777 *
778 * @param hal Context of the HAL layer
779 * @return usb_dwc_hal_port_event_t Host port event
780 */
781 usb_dwc_hal_port_event_t usb_dwc_hal_decode_intr(usb_dwc_hal_context_t *hal);
782
783 /**
784 * @brief Gets the next channel with a pending interrupt
785 *
786 * If no channel is pending an interrupt, this function will return NULL. If one or more channels are pending an
787 * interrupt, this function returns one of the channel's objects. Call this function repeatedly until it returns NULL.
788 *
789 * @param hal Context of the HAL layer
790 * @return usb_dwc_hal_chan_t* Channel object. NULL if no channel are pending an interrupt.
791 */
792 usb_dwc_hal_chan_t *usb_dwc_hal_get_chan_pending_intr(usb_dwc_hal_context_t *hal);
793
794 /**
795 * @brief Decode a particular channel's interrupt
796 *
797 * - Reads and clears the interrupt register of the channel
798 * - Returns the corresponding event for that channel
799 *
800 * @param chan_obj Channel object
801 * @note If the host port has an error (e.g., a sudden disconnect or an port error), any active channels will not
802 * receive an interrupt. Each active channel must be manually halted.
803 * @return usb_dwc_hal_chan_event_t Channel event
804 */
805 usb_dwc_hal_chan_event_t usb_dwc_hal_chan_decode_intr(usb_dwc_hal_chan_t *chan_obj);
806
807 #endif // SOC_USB_OTG_SUPPORTED
808
809 #ifdef __cplusplus
810 }
811 #endif
812