mirror of
https://github.com/espressif/esp-idf.git
synced 2025-08-09 04:25:32 +00:00
Add USB HCD
This commit adds the USB HCD (Host Controller Driver) and accompanying unit tests.
This commit is contained in:
@@ -44,6 +44,7 @@ NOTE: Thread safety is the responsibility fo the HAL user. All USB Host HAL
|
||||
|
||||
/**
|
||||
* @brief Channel states
|
||||
*
|
||||
*/
|
||||
typedef enum {
|
||||
USBH_HAL_CHAN_STATE_HALTED = 0, /**< The channel is halted. No transfer descriptor list is being executed */
|
||||
@@ -57,6 +58,7 @@ typedef enum {
|
||||
* @brief Host port HAL events
|
||||
*/
|
||||
typedef enum {
|
||||
USBH_HAL_PORT_EVENT_NONE, /**< No event occurred, or could not decode interrupt */
|
||||
USBH_HAL_PORT_EVENT_CHAN, /**< A channel event has occurred. Call the the channel event handler instead */
|
||||
USBH_HAL_PORT_EVENT_CONN, /**< The host port has detected a connection */
|
||||
USBH_HAL_PORT_EVENT_DISCONN, /**< The host port has been disconnected */
|
||||
@@ -74,7 +76,6 @@ typedef enum {
|
||||
USBH_HAL_CHAN_EVENT_SLOT_HALT, /**< The channel as completed execution of a single transfer descriptor in a list. Channel is now halted */
|
||||
USBH_HAL_CHAN_EVENT_ERROR, /**< The channel has encountered an error. Channel is now halted. */
|
||||
USBH_HAL_CHAN_EVENT_HALT_REQ, /**< The channel has been successfully halted as requested */
|
||||
USBH_HAL_CHAN_EVENT_SUDDEN_HLT, /**< The channel was suddenly halted (e.g. due to a disconnect). */
|
||||
} usbh_hal_chan_event_t;
|
||||
|
||||
// ------------------------------- HAL Errors ----------------------------------
|
||||
@@ -87,7 +88,6 @@ typedef enum {
|
||||
USBH_HAL_CHAN_ERROR_BNA, /**< Buffer Not Available error (i.e., transfer slot is unfilled */
|
||||
USBH_HAL_CHAN_ERROR_PKT_BBL, /**< Packet babbler error (packet exceeded MPS) */
|
||||
USBH_HAL_CHAN_ERROR_STALL, /**< STALL response received */
|
||||
USBH_HAL_CHAN_ERROR_AHB, /**< AHB error */
|
||||
} usbh_hal_chan_error_t;
|
||||
|
||||
// ----------------------- Transfer Descriptor Related -------------------------
|
||||
@@ -102,6 +102,11 @@ typedef enum {
|
||||
|
||||
/**
|
||||
* @brief Status value of a transfer descriptor
|
||||
*
|
||||
* A transfer descriptor's status remains unexecuted until the entire transfer
|
||||
* descriptor completes (either successfully or an error). Therefore, if a
|
||||
* channel halt is requested before a transfer descriptor completes, the
|
||||
* transfer descriptoor remains unexecuted.
|
||||
*/
|
||||
#define USBH_HAL_XFER_DESC_STS_SUCCESS USBH_LL_QTD_STATUS_SUCCESS
|
||||
#define USBH_HAL_XFER_DESC_STS_PKTERR USBH_LL_QTD_STATUS_PKTERR
|
||||
@@ -120,7 +125,9 @@ typedef struct {
|
||||
uint32_t bEndpointAddress: 8; /**< Endpoint address (containing endpoint number and direction) */
|
||||
uint32_t mps: 11; /**< Maximum Packet Size */
|
||||
uint32_t dev_addr: 8; /**< Device Address */
|
||||
uint32_t reserved3: 3;
|
||||
uint32_t ls_via_fs_hub: 1; /**< The endpoint is on a LS device that is routed through an FS hub.
|
||||
Setting this bit will lead to the addition of the PREamble packet */
|
||||
uint32_t reserved2: 2;
|
||||
};
|
||||
uint32_t val;
|
||||
};
|
||||
@@ -148,7 +155,7 @@ typedef struct {
|
||||
struct {
|
||||
union {
|
||||
struct {
|
||||
bool slot_acquired: 1; /**< The transfer descriptor list slot has been acquired */
|
||||
uint32_t slot_acquired: 1; /**< The transfer descriptor list slot has been acquired */
|
||||
uint32_t reserved7: 7;
|
||||
uint32_t cur_qtd_idx: 8; /**< Index of the first QTD in chain of QTDs being executed */
|
||||
uint32_t qtd_list_len: 8; /**< Length of QTD list in number of QTDs */
|
||||
@@ -178,8 +185,8 @@ typedef struct {
|
||||
} flags;
|
||||
//Channel related
|
||||
struct {
|
||||
int num_allocd; /**< Number of channels currently allocated */
|
||||
int chan_pend_intrs_msk; /**< Bit mask of channels with pending interrupts */
|
||||
int num_allocd; /**< Number of channels currently allocated */
|
||||
uint32_t chan_pend_intrs_msk; /**< Bit mask of channels with pending interrupts */
|
||||
usbh_hal_chan_t *hdls[USBH_HAL_NUM_CHAN]; /**< Handles of each channel. Set to NULL if channel has not been allocated */
|
||||
} channels;
|
||||
} usbh_hal_context_t;
|
||||
@@ -417,13 +424,17 @@ static inline usb_speed_t usbh_hal_port_get_conn_speed(usbh_hal_context_t *hal)
|
||||
* @brief Disable the debounce lock
|
||||
*
|
||||
* This function should be called after calling usbh_hal_port_check_if_connected()
|
||||
* and will allow connection/disconnection events to occur again.
|
||||
* and will allow connection/disconnection events to occur again. Any pending
|
||||
* connection or disconenction interrupts are cleared.
|
||||
*
|
||||
* @param hal Context of the HAL layer
|
||||
*/
|
||||
static inline void usbh_hal_disable_debounce_lock(usbh_hal_context_t *hal)
|
||||
{
|
||||
hal->flags.dbnc_lock_enabled = 0;
|
||||
//Clear Conenction and disconenction interrupt in case it triggered again
|
||||
usb_ll_intr_clear(hal->dev, USB_LL_INTR_CORE_DISCONNINT);
|
||||
usbh_ll_hprt_intr_clear(hal->dev, USBH_LL_INTR_HPRT_PRTENCHNG);
|
||||
//Reenable the hprt (connection) and disconnection interrupts
|
||||
usb_ll_en_intrs(hal->dev, USB_LL_INTR_CORE_PRTINT | USB_LL_INTR_CORE_DISCONNINT);
|
||||
}
|
||||
@@ -659,7 +670,7 @@ static inline void usbh_hal_chan_slot_acquire(usbh_hal_chan_t *chan_obj, void *x
|
||||
chan_obj->slot.owner_ctx = owner_ctx;
|
||||
chan_obj->slot.flags.cur_qtd_idx = 0; //Start from the first descriptor
|
||||
chan_obj->slot.flags.qtd_list_len = desc_list_len;
|
||||
chan_obj->slot.flags.slot_acquired = true;
|
||||
chan_obj->slot.flags.slot_acquired = 1;
|
||||
//Store the descriptor list length in the HCTSIZ register. Address of desc list is set when channel is activated
|
||||
usbh_ll_chan_set_qtd_list_len(chan_obj->regs, desc_list_len);
|
||||
}
|
||||
@@ -696,7 +707,7 @@ static inline void usbh_hal_chan_slot_release(usbh_hal_chan_t *chan_obj, void **
|
||||
assert(chan_obj->slot.flags.slot_acquired);
|
||||
*xfer_desc_list = (void *)chan_obj->slot.xfer_desc_list;
|
||||
*desc_list_len = chan_obj->slot.flags.qtd_list_len;
|
||||
chan_obj->slot.flags.slot_acquired = false;
|
||||
chan_obj->slot.flags.slot_acquired = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -734,11 +745,16 @@ static inline int usbh_hal_chan_get_next_desc_index(usbh_hal_chan_t *chan_obj)
|
||||
* active, this function will return false and users must wait for the
|
||||
* USBH_HAL_CHAN_EVENT_HALT_REQ event before treating the channel as halted.
|
||||
*
|
||||
* @note When a transfer is in progress (i.e., the channel is active) and a halt
|
||||
* is requested, the channel will halt after the next USB packet is completed.
|
||||
* If the transfer has more pending packets, the transfer will just be
|
||||
* marked as USBH_HAL_XFER_DESC_STS_NOT_EXECUTED.
|
||||
*
|
||||
* @param chan_obj Channel object
|
||||
* @return true The channel is already halted
|
||||
* @return false The halt was requested, wait for USBH_HAL_CHAN_EVENT_HALT_REQ
|
||||
*/
|
||||
bool usbh_hal_chan_request_halt(usbh_hal_chan_t *chan_obj);
|
||||
bool usbh_hal_chan_slot_request_halt(usbh_hal_chan_t *chan_obj);
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
-------------------------------- Event Handling --------------------------------
|
||||
|
@@ -283,8 +283,8 @@ static inline bool usb_ll_check_core_soft_reset(usbh_dev_t *hw)
|
||||
/**
|
||||
* @brief Reads and clears the global interrupt register
|
||||
*
|
||||
* @param hw
|
||||
* @return uint32_t
|
||||
* @param hw Start address of the DWC_OTG registers
|
||||
* @return uint32_t Mask of interrupts
|
||||
*/
|
||||
static inline uint32_t usb_ll_intr_read_and_clear(usbh_dev_t *hw)
|
||||
{
|
||||
@@ -294,6 +294,18 @@ static inline uint32_t usb_ll_intr_read_and_clear(usbh_dev_t *hw)
|
||||
return gintsts.val;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Clear specific interrupts
|
||||
*
|
||||
* @param hw Start address of the DWC_OTG registers
|
||||
* @param intr_msk Mask of interrupts to clear
|
||||
*/
|
||||
static inline void usb_ll_intr_clear(usbh_dev_t *hw, uint32_t intr_msk)
|
||||
{
|
||||
//All GINTSTS fields are either W1C or read only. So safe to write directly
|
||||
hw->gintsts_reg.val = intr_msk;
|
||||
}
|
||||
|
||||
// --------------------------- GINTMSK Register --------------------------------
|
||||
|
||||
static inline void usb_ll_en_intrs(usbh_dev_t *hw, uint32_t intr_mask)
|
||||
@@ -403,26 +415,36 @@ static inline void usbh_ll_hcfg_set_fsls_pclk_sel(usbh_dev_t *hw)
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets some default values to HCFG to operate in Host mode wiht scatter/gather DMA
|
||||
* @brief Sets some default values to HCFG to operate in Host mode with scatter/gather DMA
|
||||
*
|
||||
* @param hw
|
||||
*/
|
||||
static inline void usbh_ll_hcfg_set_defaults(usbh_dev_t *hw)
|
||||
static inline void usbh_ll_hcfg_set_defaults(usbh_dev_t *hw, usb_speed_t speed)
|
||||
{
|
||||
hw->hcfg_reg.descdma = 1; //Enable scatt/gatt
|
||||
hw->hcfg_reg.fslssupp = 1; //FS/LS supp only
|
||||
hw->hcfg_reg.fslspclksel = 1; //48MHz PHY clock
|
||||
/*
|
||||
Indicate to the OTG core what speed the PHY clock is at
|
||||
Note: It seems like our PHY has an implicit 8 divider applied when in LS mode,
|
||||
so the values of FSLSPclkSel and FrInt have to be adjusted accordingly.
|
||||
*/
|
||||
hw->hcfg_reg.fslspclksel = (speed == USB_SPEED_FULL) ? 1 : 2;
|
||||
hw->hcfg_reg.perschedena = 0; //Disable perio sched
|
||||
}
|
||||
|
||||
// ----------------------------- HFIR Register ---------------------------------
|
||||
|
||||
static inline void usbh_ll_hfir_set_defaults(usbh_dev_t *hw)
|
||||
static inline void usbh_ll_hfir_set_defaults(usbh_dev_t *hw, usb_speed_t speed)
|
||||
{
|
||||
usb_hfir_reg_t hfir;
|
||||
hfir.val = hw->hfir_reg.val;
|
||||
hfir.hfirrldctrl = 0; //Disable dynamic loading
|
||||
hfir.frint = 48000; //Set frame interval to 48000 cycles of 48MHz clock (i.e. equals to 1ms)
|
||||
/*
|
||||
Set frame interval to be equal to 1ms
|
||||
Note: It seems like our PHY has an implicit 8 divider applied when in LS mode,
|
||||
so the values of FSLSPclkSel and FrInt have to be adjusted accordingly.
|
||||
*/
|
||||
hfir.frint = (speed == USB_SPEED_FULL) ? 48000 : 6000;
|
||||
hw->hfir_reg.val = hfir.val;
|
||||
}
|
||||
|
||||
@@ -611,6 +633,13 @@ static inline uint32_t usbh_ll_hprt_intr_read_and_clear(usbh_dev_t *hw)
|
||||
return (hprt.val & (USBH_LL_HPRT_W1C_MSK & ~(USBH_LL_HPRT_ENA_MSK)));
|
||||
}
|
||||
|
||||
static inline void usbh_ll_hprt_intr_clear(usbh_dev_t *hw, uint32_t intr_mask)
|
||||
{
|
||||
usb_hprt_reg_t hprt;
|
||||
hprt.val = hw->hprt_reg.val;
|
||||
hw->hprt_reg.val = ((hprt.val & ~USBH_LL_HPRT_ENA_MSK) & ~USBH_LL_HPRT_W1C_MSK) | intr_mask;
|
||||
}
|
||||
|
||||
//Per Channel registers
|
||||
|
||||
// --------------------------- HCCHARi Register --------------------------------
|
||||
@@ -665,9 +694,11 @@ static inline void usbh_ll_chan_set_ep_type(volatile usb_host_chan_regs_t *chan,
|
||||
}
|
||||
}
|
||||
|
||||
static inline void usbh_ll_chan_set_ls(volatile usb_host_chan_regs_t *chan)
|
||||
//Indicates whether channel is commuunicating with a LS device connected via a FS hub. Setting this bit to 1 will cause
|
||||
//each packet to be preceded by a PREamble packet
|
||||
static inline void usbh_ll_chan_set_lspddev(volatile usb_host_chan_regs_t *chan, bool is_ls)
|
||||
{
|
||||
chan->hcchar_reg.lspddev = 1;
|
||||
chan->hcchar_reg.lspddev = is_ls;
|
||||
}
|
||||
|
||||
static inline void usbh_ll_chan_set_dir(volatile usb_host_chan_regs_t *chan, bool is_in)
|
||||
@@ -685,11 +716,12 @@ static inline void usbh_ll_chan_set_mps(volatile usb_host_chan_regs_t *chan, uin
|
||||
chan->hcchar_reg.mps = mps;
|
||||
}
|
||||
|
||||
static inline void usbh_ll_chan_hcchar_init(volatile usb_host_chan_regs_t *chan, int dev_addr, int ep_num, int mps, usb_xfer_type_t type, bool is_in)
|
||||
static inline void usbh_ll_chan_hcchar_init(volatile usb_host_chan_regs_t *chan, int dev_addr, int ep_num, int mps, usb_xfer_type_t type, bool is_in, bool is_ls)
|
||||
{
|
||||
//Sets all persistent fields of the channel over its lifetime
|
||||
usbh_ll_chan_set_dev_addr(chan, dev_addr);
|
||||
usbh_ll_chan_set_ep_type(chan, type);
|
||||
usbh_ll_chan_set_lspddev(chan, is_ls);
|
||||
usbh_ll_chan_set_dir(chan, is_in);
|
||||
usbh_ll_chan_set_ep_num(chan, ep_num);
|
||||
usbh_ll_chan_set_mps(chan, mps);
|
||||
|
@@ -84,22 +84,29 @@ _Static_assert((RX_FIFO_LEN + NPTX_FIFO_LEN + PTX_FIFO_LEN + REG_FIFO_LEN) <= HW
|
||||
* The following channel interrupt bits are currently checked (in order LSB to MSB)
|
||||
* - USBH_LL_INTR_CHAN_XFERCOMPL
|
||||
* - USBH_LL_INTR_CHAN_CHHLTD
|
||||
* - USBH_LL_INTR_CHAN_AHBERR
|
||||
* - USBH_LL_INTR_CHAN_STALL
|
||||
* - USBH_LL_INTR_CHAN_BBLEER
|
||||
* - USBH_LL_INTR_CHAN_BNAINTR
|
||||
* - USBH_LL_INTR_CHAN_XCS_XACT_ERR
|
||||
*
|
||||
* Note the following points about channel interrupts:
|
||||
* - Not all bits are unmaskable under scatter/gather
|
||||
* - Those bits proxy their interrupt through the USBH_LL_INTR_CHAN_CHHLTD bit
|
||||
* - USBH_LL_INTR_CHAN_XCS_XACT_ERR is always unmasked
|
||||
* - When USBH_LL_INTR_CHAN_BNAINTR occurs, USBH_LL_INTR_CHAN_CHHLTD will NOT.
|
||||
* - USBH_LL_INTR_CHAN_AHBERR doesn't actually ever happen on our system )i.e., ESP32S2 and later):
|
||||
* - If the QTD list's starting address is an invalid address (e.g., NULL), the core will attempt to fetch that
|
||||
* address for a transfer descriptor and probably gets all zeroes. It will interpret the zero as a bad QTD and
|
||||
* return a USBH_LL_INTR_CHAN_BNAINTR instead.
|
||||
* - If the QTD's buffer pointer is an invalid address, the core will attempt to read/write data to/from that
|
||||
* invalid buffer address with NO INDICATION OF ERROR. The transfer will be acknowledged and treated as
|
||||
* successful. Bad buffer pointers MUST BE CHECKED FROM HIGHER LAYERS INSTEAD.
|
||||
*/
|
||||
#define CHAN_INTRS_EN_MSK (USBH_LL_INTR_CHAN_XFERCOMPL | \
|
||||
USBH_LL_INTR_CHAN_CHHLTD | \
|
||||
USBH_LL_INTR_CHAN_BNAINTR)
|
||||
|
||||
#define CHAN_INTRS_ERROR_MSK (USBH_LL_INTR_CHAN_AHBERR | \
|
||||
USBH_LL_INTR_CHAN_STALL | \
|
||||
#define CHAN_INTRS_ERROR_MSK (USBH_LL_INTR_CHAN_STALL | \
|
||||
USBH_LL_INTR_CHAN_BBLEER | \
|
||||
USBH_LL_INTR_CHAN_BNAINTR | \
|
||||
USBH_LL_INTR_CHAN_XCS_XACT_ERR)
|
||||
@@ -164,8 +171,11 @@ void usbh_hal_core_soft_reset(usbh_hal_context_t *hal)
|
||||
}
|
||||
//Set the default bits
|
||||
set_defaults(hal);
|
||||
//Clear all the flags
|
||||
//Clear all the flags and channels
|
||||
hal->flags.val = 0;
|
||||
hal->channels.num_allocd = 0;
|
||||
hal->channels.chan_pend_intrs_msk = 0;
|
||||
memset(hal->channels.hdls, 0, sizeof(usbh_hal_chan_t *) * USBH_HAL_NUM_CHAN);
|
||||
}
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
@@ -181,11 +191,12 @@ static inline void debounce_lock_enable(usbh_hal_context_t *hal)
|
||||
|
||||
void usbh_hal_port_enable(usbh_hal_context_t *hal)
|
||||
{
|
||||
usb_speed_t speed = usbh_ll_hprt_get_speed(hal->dev);
|
||||
//Host Configuration
|
||||
usbh_ll_hcfg_set_defaults(hal->dev);
|
||||
usbh_ll_hcfg_set_defaults(hal->dev, speed);
|
||||
//Todo: Set frame list entries and ena per sched
|
||||
//Configure HFIR
|
||||
usbh_ll_hfir_set_defaults(hal->dev);
|
||||
usbh_ll_hfir_set_defaults(hal->dev, speed);
|
||||
//Config FIFO sizes
|
||||
usb_ll_set_rx_fifo_size(hal->dev, RX_FIFO_LEN);
|
||||
usb_ll_set_nptx_fifo_size(hal->dev, RX_FIFO_LEN, NPTX_FIFO_LEN);
|
||||
@@ -225,7 +236,7 @@ bool usbh_hal_chan_alloc(usbh_hal_context_t *hal, usbh_hal_chan_t *chan_obj, voi
|
||||
usbh_ll_chan_intr_read_and_clear(chan_obj->regs); //Clear the interrupt bits for that channel
|
||||
usbh_ll_haintmsk_en_chan_intr(hal->dev, 1 << chan_obj->flags.chan_idx);
|
||||
usbh_ll_chan_set_intr_mask(chan_obj->regs, CHAN_INTRS_EN_MSK); //Unmask interrupts for this channel
|
||||
usbh_ll_chan_set_pid(chan_obj->regs, 0); //Set the initial PID to zero
|
||||
usbh_ll_chan_set_pid(chan_obj->regs, 0); //Set the initial PID to zero
|
||||
usbh_ll_chan_hctsiz_init(chan_obj->regs); //Set the non changing parts of the HCTSIZ registers (e.g., do_ping and sched info)
|
||||
return true;
|
||||
}
|
||||
@@ -235,8 +246,8 @@ void usbh_hal_chan_free(usbh_hal_context_t *hal, usbh_hal_chan_t *chan_obj)
|
||||
{
|
||||
//Can only free a channel when in the disabled state and descriptor list released
|
||||
assert(!chan_obj->slot.flags.slot_acquired
|
||||
&& !chan_obj->flags.active
|
||||
&& !chan_obj->flags.error_pending);
|
||||
&& !chan_obj->flags.active
|
||||
&& !chan_obj->flags.error_pending);
|
||||
//Deallocate channel
|
||||
hal->channels.hdls[chan_obj->flags.chan_idx] = NULL;
|
||||
hal->channels.num_allocd--;
|
||||
@@ -255,7 +266,8 @@ void usbh_hal_chan_set_ep_char(usbh_hal_chan_t *chan_obj, usbh_hal_ep_char_t *ep
|
||||
ep_char->bEndpointAddress & USB_B_ENDPOINT_ADDRESS_EP_NUM_MASK,
|
||||
ep_char->mps,
|
||||
ep_char->type,
|
||||
ep_char->bEndpointAddress & USB_B_ENDPOINT_ADDRESS_EP_DIR_MASK);
|
||||
ep_char->bEndpointAddress & USB_B_ENDPOINT_ADDRESS_EP_DIR_MASK,
|
||||
ep_char->ls_via_fs_hub);
|
||||
}
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
@@ -278,11 +290,11 @@ void usbh_hal_chan_activate(usbh_hal_chan_t *chan_obj, int num_to_skip)
|
||||
usbh_ll_chan_start(chan_obj->regs);
|
||||
}
|
||||
|
||||
bool usbh_hal_chan_request_halt(usbh_hal_chan_t *chan_obj)
|
||||
bool usbh_hal_chan_slot_request_halt(usbh_hal_chan_t *chan_obj)
|
||||
{
|
||||
//Cannot request halt on a channel that is pending error handling
|
||||
assert(!chan_obj->flags.error_pending);
|
||||
if (usbh_ll_chan_is_active(chan_obj->regs)) {
|
||||
if (usbh_ll_chan_is_active(chan_obj->regs) || chan_obj->flags.active) {
|
||||
usbh_ll_chan_halt(chan_obj->regs);
|
||||
chan_obj->flags.halt_requested = 1;
|
||||
return false;
|
||||
@@ -294,6 +306,16 @@ bool usbh_hal_chan_request_halt(usbh_hal_chan_t *chan_obj)
|
||||
-------------------------------- Event Handling --------------------------------
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
//When a device on the port is no longer valid (e.g., disconnect, port error). All channels are no longer valid
|
||||
static void chan_all_halt(usbh_hal_context_t *hal)
|
||||
{
|
||||
for (int i = 0; i < USBH_HAL_NUM_CHAN; i++) {
|
||||
if (hal->channels.hdls[i] != NULL) {
|
||||
hal->channels.hdls[i]->flags.active = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
usbh_hal_port_event_t usbh_hal_decode_intr(usbh_hal_context_t *hal)
|
||||
{
|
||||
uint32_t intrs_core = usb_ll_intr_read_and_clear(hal->dev); //Read and clear core interrupts
|
||||
@@ -304,7 +326,7 @@ usbh_hal_port_event_t usbh_hal_decode_intr(usbh_hal_context_t *hal)
|
||||
}
|
||||
//Note: Do not change order of checks. Regressing events (e.g. enable -> disabled, connected -> connected)
|
||||
//always take precendance. ENABLED < DISABLED < CONN < DISCONN < OVRCUR
|
||||
usbh_hal_port_event_t event = -1;
|
||||
usbh_hal_port_event_t event = USBH_HAL_PORT_EVENT_NONE;
|
||||
|
||||
//Check if this is a core or port event
|
||||
if ((intrs_core & CORE_EVENTS_INTRS_MSK) || (intrs_port & PORT_EVENTS_INTRS_MSK)) {
|
||||
@@ -312,11 +334,13 @@ usbh_hal_port_event_t usbh_hal_decode_intr(usbh_hal_context_t *hal)
|
||||
if (intrs_core & USB_LL_INTR_CORE_DISCONNINT) {
|
||||
event = USBH_HAL_PORT_EVENT_DISCONN;
|
||||
debounce_lock_enable(hal);
|
||||
chan_all_halt(hal); //All channels are halted on a disconnect
|
||||
//Mask the port connection and disconnection interrupts to prevent repeated triggering
|
||||
} else if (intrs_port & USBH_LL_INTR_HPRT_PRTOVRCURRCHNG) {
|
||||
//Check if this is an overcurrent or an overcurrent cleared
|
||||
if (usbh_ll_hprt_get_port_overcur(hal->dev)) {
|
||||
event = USBH_HAL_PORT_EVENT_OVRCUR;
|
||||
chan_all_halt(hal); //All channels are halted on an overcurrent
|
||||
} else {
|
||||
event = USBH_HAL_PORT_EVENT_OVRCUR_CLR;
|
||||
}
|
||||
@@ -325,13 +349,15 @@ usbh_hal_port_event_t usbh_hal_decode_intr(usbh_hal_context_t *hal)
|
||||
event = USBH_HAL_PORT_EVENT_ENABLED;
|
||||
} else { //Host port has been disabled
|
||||
event = USBH_HAL_PORT_EVENT_DISABLED;
|
||||
chan_all_halt(hal); //All channels are halted when the port is disabled
|
||||
}
|
||||
} else if (intrs_port & USBH_LL_INTR_HPRT_PRTCONNDET && !hal->flags.dbnc_lock_enabled) {
|
||||
event = USBH_HAL_PORT_EVENT_CONN;
|
||||
debounce_lock_enable(hal);
|
||||
}
|
||||
}
|
||||
if (intrs_core & USB_LL_INTR_CORE_HCHINT) {
|
||||
//Port events always take precendance over channel events
|
||||
if (event == USBH_HAL_PORT_EVENT_NONE && (intrs_core & USB_LL_INTR_CORE_HCHINT)) {
|
||||
//One or more channels have pending interrupts. Store the mask of those channels
|
||||
hal->channels.chan_pend_intrs_msk = usbh_ll_get_chan_intrs_msk(hal->dev);
|
||||
event = USBH_HAL_PORT_EVENT_CHAN;
|
||||
@@ -355,17 +381,15 @@ usbh_hal_chan_event_t usbh_hal_chan_decode_intr(usbh_hal_chan_t *chan_obj)
|
||||
{
|
||||
uint32_t chan_intrs = usbh_ll_chan_intr_read_and_clear(chan_obj->regs);
|
||||
usbh_hal_chan_event_t chan_event;
|
||||
//Currently, all cases where channel interrupts occur will also halt the channel
|
||||
assert(chan_intrs & USBH_LL_INTR_CHAN_CHHLTD);
|
||||
//Currently, all cases where channel interrupts occur will also halt the channel, except for BNA
|
||||
assert(chan_intrs & (USBH_LL_INTR_CHAN_CHHLTD | USBH_LL_INTR_CHAN_BNAINTR));
|
||||
chan_obj->flags.active = 0;
|
||||
//Note: Do not change the current checking order of checks. Certain interrupts (e.g., errors) have precedence over others
|
||||
if (chan_intrs & CHAN_INTRS_ERROR_MSK) { //One of the error interrupts has occurred.
|
||||
//Note: Errors are uncommon, so we check against the entire interrupt mask to reduce frequency of entering this call path
|
||||
//Store the error in hal context
|
||||
usbh_hal_chan_error_t error;
|
||||
if (chan_intrs & USBH_LL_INTR_CHAN_AHBERR) {
|
||||
error = USBH_HAL_CHAN_ERROR_AHB;
|
||||
} else if (chan_intrs & USBH_LL_INTR_CHAN_STALL) {
|
||||
if (chan_intrs & USBH_LL_INTR_CHAN_STALL) {
|
||||
error = USBH_HAL_CHAN_ERROR_STALL;
|
||||
} else if (chan_intrs & USBH_LL_INTR_CHAN_BBLEER) {
|
||||
error = USBH_HAL_CHAN_ERROR_PKT_BBL;
|
||||
@@ -393,8 +417,8 @@ usbh_hal_chan_event_t usbh_hal_chan_decode_intr(usbh_hal_chan_t *chan_obj)
|
||||
chan_event = USBH_HAL_CHAN_EVENT_SLOT_HALT;
|
||||
}
|
||||
} else {
|
||||
//Channel halted suddenly (i.e,, a disconnect)
|
||||
chan_event = USBH_HAL_CHAN_EVENT_SUDDEN_HLT;
|
||||
//Should never reach this point
|
||||
abort();
|
||||
}
|
||||
return chan_event;
|
||||
}
|
||||
|
Reference in New Issue
Block a user