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