Merge branch 'master' into test-mode-support
This commit is contained in:
@@ -97,28 +97,170 @@ static uint16_t ep0_pending[2]; // Index determines direction as t
|
||||
|
||||
// TX FIFO RAM allocation so far in words - RX FIFO size is readily available from dwc2->grxfsiz
|
||||
static uint16_t _allocated_fifo_words_tx; // TX FIFO size in words (IN EPs)
|
||||
static bool _out_ep_closed; // Flag to check if RX FIFO size needs an update (reduce its size)
|
||||
|
||||
// SOF enabling flag - required for SOF to not get disabled in ISR when SOF was enabled by
|
||||
static bool _sof_en;
|
||||
|
||||
// Calculate the RX FIFO size according to recommendations from reference manual
|
||||
// Calculate the RX FIFO size according to minimum recommendations from reference manual
|
||||
// RxFIFO = (5 * number of control endpoints + 8) +
|
||||
// ((largest USB packet used / 4) + 1 for status information) +
|
||||
// (2 * number of OUT endpoints) + 1 for Global NAK
|
||||
// with number of control endpoints = 1 we have
|
||||
// RxFIFO = 15 + (largest USB packet used / 4) + 2 * number of OUT endpoints
|
||||
// we double the largest USB packet size to be able to hold up to 2 packets
|
||||
static inline uint16_t calc_grxfsiz(uint16_t max_ep_size, uint8_t ep_count) {
|
||||
return 15 + 2 * (max_ep_size / 4) + 2 * ep_count;
|
||||
}
|
||||
|
||||
static void update_grxfsiz(uint8_t rhport) {
|
||||
TU_ATTR_ALWAYS_INLINE static inline void fifo_flush_tx(dwc2_regs_t* dwc2, uint8_t epnum) {
|
||||
// flush TX fifo and wait for it cleared
|
||||
dwc2->grstctl = GRSTCTL_TXFFLSH | (epnum << GRSTCTL_TXFNUM_Pos);
|
||||
while (dwc2->grstctl & GRSTCTL_TXFFLSH_Msk) {}
|
||||
}
|
||||
TU_ATTR_ALWAYS_INLINE static inline void fifo_flush_rx(dwc2_regs_t* dwc2) {
|
||||
// flush RX fifo and wait for it cleared
|
||||
dwc2->grstctl = GRSTCTL_RXFFLSH;
|
||||
while (dwc2->grstctl & GRSTCTL_RXFFLSH_Msk) {}
|
||||
}
|
||||
|
||||
static bool fifo_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t packet_size) {
|
||||
dwc2_regs_t* dwc2 = DWC2_REG(rhport);
|
||||
uint8_t const ep_count = _dwc2_controller[rhport].ep_count;
|
||||
uint8_t const epnum = tu_edpt_number(ep_addr);
|
||||
uint8_t const dir = tu_edpt_dir(ep_addr);
|
||||
|
||||
// Determine largest EP size for RX FIFO
|
||||
uint16_t max_epsize = 0;
|
||||
for (uint8_t epnum = 0; epnum < ep_count; epnum++) {
|
||||
max_epsize = tu_max16(max_epsize, xfer_status[epnum][TUSB_DIR_OUT].max_size);
|
||||
TU_ASSERT(epnum < ep_count);
|
||||
|
||||
uint16_t fifo_size = tu_div_ceil(packet_size, 4);
|
||||
|
||||
// "USB Data FIFOs" section in reference manual
|
||||
// Peripheral FIFO architecture
|
||||
//
|
||||
// --------------- 320 or 1024 ( 1280 or 4096 bytes )
|
||||
// | IN FIFO 0 |
|
||||
// --------------- (320 or 1024) - 16
|
||||
// | IN FIFO 1 |
|
||||
// --------------- (320 or 1024) - 16 - x
|
||||
// | . . . . |
|
||||
// --------------- (320 or 1024) - 16 - x - y - ... - z
|
||||
// | IN FIFO MAX |
|
||||
// ---------------
|
||||
// | FREE |
|
||||
// --------------- GRXFSIZ
|
||||
// | OUT FIFO |
|
||||
// | ( Shared ) |
|
||||
// --------------- 0
|
||||
//
|
||||
// In FIFO is allocated by following rules:
|
||||
// - IN EP 1 gets FIFO 1, IN EP "n" gets FIFO "n".
|
||||
if (dir == TUSB_DIR_OUT) {
|
||||
// Calculate required size of RX FIFO
|
||||
uint16_t const sz = calc_grxfsiz(4 * fifo_size, ep_count);
|
||||
|
||||
// If size_rx needs to be extended check if possible and if so enlarge it
|
||||
if (dwc2->grxfsiz < sz) {
|
||||
TU_ASSERT(sz + _allocated_fifo_words_tx <= _dwc2_controller[rhport].ep_fifo_size / 4);
|
||||
|
||||
// Enlarge RX FIFO
|
||||
dwc2->grxfsiz = sz;
|
||||
}
|
||||
} else {
|
||||
// Note if The TXFELVL is configured as half empty. In order
|
||||
// to be able to write a packet at that point, the fifo must be twice the max_size.
|
||||
if ((dwc2->gahbcfg & GAHBCFG_TXFELVL) == 0) {
|
||||
fifo_size *= 2;
|
||||
}
|
||||
|
||||
// Check if free space is available
|
||||
TU_ASSERT(_allocated_fifo_words_tx + fifo_size + dwc2->grxfsiz <= _dwc2_controller[rhport].ep_fifo_size / 4);
|
||||
_allocated_fifo_words_tx += fifo_size;
|
||||
TU_LOG(DWC2_DEBUG, " Allocated %u bytes at offset %" PRIu32, fifo_size * 4,
|
||||
_dwc2_controller[rhport].ep_fifo_size - _allocated_fifo_words_tx * 4);
|
||||
|
||||
// DIEPTXF starts at FIFO #1.
|
||||
// Both TXFD and TXSA are in unit of 32-bit words.
|
||||
dwc2->dieptxf[epnum - 1] = (fifo_size << DIEPTXF_INEPTXFD_Pos) |
|
||||
(_dwc2_controller[rhport].ep_fifo_size / 4 - _allocated_fifo_words_tx);
|
||||
}
|
||||
|
||||
// Update size of RX FIFO
|
||||
dwc2->grxfsiz = calc_grxfsiz(max_epsize, ep_count);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void edpt_activate(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc) {
|
||||
dwc2_regs_t* dwc2 = DWC2_REG(rhport);
|
||||
uint8_t const epnum = tu_edpt_number(p_endpoint_desc->bEndpointAddress);
|
||||
uint8_t const dir = tu_edpt_dir(p_endpoint_desc->bEndpointAddress);
|
||||
|
||||
xfer_ctl_t* xfer = XFER_CTL_BASE(epnum, dir);
|
||||
xfer->max_size = tu_edpt_packet_size(p_endpoint_desc);
|
||||
xfer->interval = p_endpoint_desc->bInterval;
|
||||
|
||||
// USBAEP, EPTYP, SD0PID_SEVNFRM, MPSIZ are the same for IN and OUT endpoints.
|
||||
uint32_t const dxepctl = (1 << DOEPCTL_USBAEP_Pos) |
|
||||
(p_endpoint_desc->bmAttributes.xfer << DOEPCTL_EPTYP_Pos) |
|
||||
(p_endpoint_desc->bmAttributes.xfer != TUSB_XFER_ISOCHRONOUS ? DOEPCTL_SD0PID_SEVNFRM : 0) |
|
||||
(xfer->max_size << DOEPCTL_MPSIZ_Pos);
|
||||
|
||||
if (dir == TUSB_DIR_OUT) {
|
||||
dwc2->epout[epnum].doepctl = dxepctl;
|
||||
dwc2->daintmsk |= TU_BIT(DAINTMSK_OEPM_Pos + epnum);
|
||||
} else {
|
||||
dwc2->epin[epnum].diepctl = dxepctl | (epnum << DIEPCTL_TXFNUM_Pos);
|
||||
dwc2->daintmsk |= (1 << (DAINTMSK_IEPM_Pos + epnum));
|
||||
}
|
||||
}
|
||||
|
||||
static void edpt_disable(uint8_t rhport, uint8_t ep_addr, bool stall) {
|
||||
(void) rhport;
|
||||
|
||||
dwc2_regs_t* dwc2 = DWC2_REG(rhport);
|
||||
uint8_t const epnum = tu_edpt_number(ep_addr);
|
||||
uint8_t const dir = tu_edpt_dir(ep_addr);
|
||||
|
||||
if (dir == TUSB_DIR_IN) {
|
||||
dwc2_epin_t* epin = dwc2->epin;
|
||||
|
||||
// Only disable currently enabled non-control endpoint
|
||||
if ((epnum == 0) || !(epin[epnum].diepctl & DIEPCTL_EPENA)) {
|
||||
epin[epnum].diepctl |= DIEPCTL_SNAK | (stall ? DIEPCTL_STALL : 0);
|
||||
} else {
|
||||
// Stop transmitting packets and NAK IN xfers.
|
||||
epin[epnum].diepctl |= DIEPCTL_SNAK;
|
||||
while ((epin[epnum].diepint & DIEPINT_INEPNE) == 0) {}
|
||||
|
||||
// Disable the endpoint.
|
||||
epin[epnum].diepctl |= DIEPCTL_EPDIS | (stall ? DIEPCTL_STALL : 0);
|
||||
while ((epin[epnum].diepint & DIEPINT_EPDISD_Msk) == 0) {}
|
||||
|
||||
epin[epnum].diepint = DIEPINT_EPDISD;
|
||||
}
|
||||
|
||||
// Flush the FIFO, and wait until we have confirmed it cleared.
|
||||
fifo_flush_tx(dwc2, epnum);
|
||||
} else {
|
||||
dwc2_epout_t* epout = dwc2->epout;
|
||||
|
||||
// Only disable currently enabled non-control endpoint
|
||||
if ((epnum == 0) || !(epout[epnum].doepctl & DOEPCTL_EPENA)) {
|
||||
epout[epnum].doepctl |= stall ? DOEPCTL_STALL : 0;
|
||||
} else {
|
||||
// Asserting GONAK is required to STALL an OUT endpoint.
|
||||
// Simpler to use polling here, we don't use the "B"OUTNAKEFF interrupt
|
||||
// anyway, and it can't be cleared by user code. If this while loop never
|
||||
// finishes, we have bigger problems than just the stack.
|
||||
dwc2->dctl |= DCTL_SGONAK;
|
||||
while ((dwc2->gintsts & GINTSTS_BOUTNAKEFF_Msk) == 0) {}
|
||||
|
||||
// Ditto here- disable the endpoint.
|
||||
epout[epnum].doepctl |= DOEPCTL_EPDIS | (stall ? DOEPCTL_STALL : 0);
|
||||
while ((epout[epnum].doepint & DOEPINT_EPDISD_Msk) == 0) {}
|
||||
|
||||
epout[epnum].doepint = DOEPINT_EPDISD;
|
||||
|
||||
// Allow other OUT endpoints to keep receiving.
|
||||
dwc2->dctl |= DCTL_CGONAK;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Start of Bus Reset
|
||||
@@ -127,7 +269,6 @@ static void bus_reset(uint8_t rhport) {
|
||||
uint8_t const ep_count = _dwc2_controller[rhport].ep_count;
|
||||
|
||||
tu_memclr(xfer_status, sizeof(xfer_status));
|
||||
_out_ep_closed = false;
|
||||
|
||||
_sof_en = false;
|
||||
|
||||
@@ -139,7 +280,17 @@ static void bus_reset(uint8_t rhport) {
|
||||
dwc2->epout[n].doepctl |= DOEPCTL_SNAK;
|
||||
}
|
||||
|
||||
// 2. Set up interrupt mask
|
||||
// 2. Disable all IN endpoints
|
||||
for (uint8_t n = 0; n < ep_count; n++) {
|
||||
if (dwc2->epin[n].diepctl & DIEPCTL_EPENA) {
|
||||
dwc2->epin[n].diepctl |= DIEPCTL_SNAK | DIEPCTL_EPDIS;
|
||||
}
|
||||
}
|
||||
|
||||
fifo_flush_tx(dwc2, 0x10); // all tx fifo
|
||||
fifo_flush_rx(dwc2);
|
||||
|
||||
// 3. Set up interrupt mask
|
||||
dwc2->daintmsk = TU_BIT(DAINTMSK_OEPM_Pos) | TU_BIT(DAINTMSK_IEPM_Pos);
|
||||
dwc2->doepmsk = DOEPMSK_STUPM | DOEPMSK_XFRCM;
|
||||
dwc2->diepmsk = DIEPMSK_TOM | DIEPMSK_XFRCM;
|
||||
@@ -269,18 +420,16 @@ static void edpt_schedule_packets(uint8_t rhport, uint8_t const epnum, uint8_t c
|
||||
/* Controller API
|
||||
*------------------------------------------------------------------*/
|
||||
#if CFG_TUSB_DEBUG >= DWC2_DEBUG
|
||||
|
||||
void print_dwc2_info(dwc2_regs_t* dwc2) {
|
||||
// print guid, gsnpsid, ghwcfg1, ghwcfg2, ghwcfg3, ghwcfg4
|
||||
// use dwc2_info.py/md for bit-field value and comparison with other ports
|
||||
volatile uint32_t const* p = (volatile uint32_t const*) &dwc2->guid;
|
||||
TU_LOG(DWC2_DEBUG, "guid, gsnpsid, ghwcfg1, ghwcfg2, ghwcfg3, ghwcfg4\r\n");
|
||||
for (size_t i = 0; i < 5; i++) {
|
||||
TU_LOG(DWC2_DEBUG, "0x%08lX, ", p[i]);
|
||||
TU_LOG(DWC2_DEBUG, "0x%08" PRIX32 ", ", p[i]);
|
||||
}
|
||||
TU_LOG(DWC2_DEBUG, "0x%08lX\r\n", p[5]);
|
||||
TU_LOG(DWC2_DEBUG, "0x%08" PRIX32 "\r\n", p[5]);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static void reset_core(dwc2_regs_t* dwc2) {
|
||||
@@ -298,11 +447,15 @@ static void reset_core(dwc2_regs_t* dwc2) {
|
||||
}
|
||||
|
||||
static bool phy_hs_supported(dwc2_regs_t* dwc2) {
|
||||
// note: esp32 incorrect report its hs_phy_type as utmi
|
||||
(void) dwc2;
|
||||
|
||||
#if TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3)
|
||||
// note: esp32 incorrect report its hs_phy_type as utmi
|
||||
return false;
|
||||
#elif !TUD_OPT_HIGH_SPEED
|
||||
return false;
|
||||
#else
|
||||
return TUD_OPT_HIGH_SPEED && dwc2->ghwcfg2_bm.hs_phy_type != HS_PHY_TYPE_NONE;
|
||||
return dwc2->ghwcfg2_bm.hs_phy_type != HS_PHY_TYPE_NONE;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -447,13 +600,8 @@ void dcd_init(uint8_t rhport) {
|
||||
// (non zero-length packet), send STALL back and discard.
|
||||
dwc2->dcfg |= DCFG_NZLSOHSK;
|
||||
|
||||
// flush all TX fifo and wait for it cleared
|
||||
dwc2->grstctl = GRSTCTL_TXFFLSH | (0x10u << GRSTCTL_TXFNUM_Pos);
|
||||
while (dwc2->grstctl & GRSTCTL_TXFFLSH_Msk) {}
|
||||
|
||||
// flush RX fifo and wait for it cleared
|
||||
dwc2->grstctl = GRSTCTL_RXFFLSH;
|
||||
while (dwc2->grstctl & GRSTCTL_RXFFLSH_Msk) {}
|
||||
fifo_flush_tx(dwc2, 0x10); // all tx fifo
|
||||
fifo_flush_rx(dwc2);
|
||||
|
||||
// Clear all interrupts
|
||||
uint32_t int_mask = dwc2->gintsts;
|
||||
@@ -462,11 +610,12 @@ void dcd_init(uint8_t rhport) {
|
||||
dwc2->gotgint |= int_mask;
|
||||
|
||||
// Required as part of core initialization.
|
||||
// TODO: How should mode mismatch be handled? It will cause
|
||||
// the core to stop working/require reset.
|
||||
dwc2->gintmsk = GINTMSK_OTGINT | GINTMSK_MMISM | GINTMSK_RXFLVLM |
|
||||
dwc2->gintmsk = GINTMSK_OTGINT | GINTMSK_RXFLVLM |
|
||||
GINTMSK_USBSUSPM | GINTMSK_USBRST | GINTMSK_ENUMDNEM | GINTMSK_WUIM;
|
||||
|
||||
// Configure TX FIFO empty level for interrupt. Default is complete empty
|
||||
dwc2->gahbcfg |= GAHBCFG_TXFELVL;
|
||||
|
||||
// Enable global interrupt
|
||||
dwc2->gahbcfg |= GAHBCFG_GINT;
|
||||
|
||||
@@ -547,84 +696,8 @@ void dcd_sof_enable(uint8_t rhport, bool en) {
|
||||
*------------------------------------------------------------------*/
|
||||
|
||||
bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const* desc_edpt) {
|
||||
(void) rhport;
|
||||
|
||||
dwc2_regs_t* dwc2 = DWC2_REG(rhport);
|
||||
uint8_t const ep_count = _dwc2_controller[rhport].ep_count;
|
||||
|
||||
uint8_t const epnum = tu_edpt_number(desc_edpt->bEndpointAddress);
|
||||
uint8_t const dir = tu_edpt_dir(desc_edpt->bEndpointAddress);
|
||||
|
||||
TU_ASSERT(epnum < ep_count);
|
||||
|
||||
xfer_ctl_t* xfer = XFER_CTL_BASE(epnum, dir);
|
||||
xfer->max_size = tu_edpt_packet_size(desc_edpt);
|
||||
xfer->interval = desc_edpt->bInterval;
|
||||
|
||||
uint16_t const fifo_size = tu_div_ceil(xfer->max_size, 4);
|
||||
|
||||
if (dir == TUSB_DIR_OUT) {
|
||||
// Calculate required size of RX FIFO
|
||||
uint16_t const sz = calc_grxfsiz(4 * fifo_size, ep_count);
|
||||
|
||||
// If size_rx needs to be extended check if possible and if so enlarge it
|
||||
if (dwc2->grxfsiz < sz) {
|
||||
TU_ASSERT(sz + _allocated_fifo_words_tx <= _dwc2_controller[rhport].ep_fifo_size / 4);
|
||||
|
||||
// Enlarge RX FIFO
|
||||
dwc2->grxfsiz = sz;
|
||||
}
|
||||
|
||||
dwc2->epout[epnum].doepctl |= (1 << DOEPCTL_USBAEP_Pos) |
|
||||
(desc_edpt->bmAttributes.xfer << DOEPCTL_EPTYP_Pos) |
|
||||
(desc_edpt->bmAttributes.xfer != TUSB_XFER_ISOCHRONOUS ? DOEPCTL_SD0PID_SEVNFRM : 0) |
|
||||
(xfer->max_size << DOEPCTL_MPSIZ_Pos);
|
||||
|
||||
dwc2->daintmsk |= TU_BIT(DAINTMSK_OEPM_Pos + epnum);
|
||||
} else {
|
||||
// "USB Data FIFOs" section in reference manual
|
||||
// Peripheral FIFO architecture
|
||||
//
|
||||
// --------------- 320 or 1024 ( 1280 or 4096 bytes )
|
||||
// | IN FIFO 0 |
|
||||
// --------------- (320 or 1024) - 16
|
||||
// | IN FIFO 1 |
|
||||
// --------------- (320 or 1024) - 16 - x
|
||||
// | . . . . |
|
||||
// --------------- (320 or 1024) - 16 - x - y - ... - z
|
||||
// | IN FIFO MAX |
|
||||
// ---------------
|
||||
// | FREE |
|
||||
// --------------- GRXFSIZ
|
||||
// | OUT FIFO |
|
||||
// | ( Shared ) |
|
||||
// --------------- 0
|
||||
//
|
||||
// In FIFO is allocated by following rules:
|
||||
// - IN EP 1 gets FIFO 1, IN EP "n" gets FIFO "n".
|
||||
|
||||
// Check if free space is available
|
||||
TU_ASSERT(_allocated_fifo_words_tx + fifo_size + dwc2->grxfsiz <= _dwc2_controller[rhport].ep_fifo_size / 4);
|
||||
|
||||
_allocated_fifo_words_tx += fifo_size;
|
||||
|
||||
TU_LOG(DWC2_DEBUG, " Allocated %u bytes at offset %lu", fifo_size * 4,
|
||||
_dwc2_controller[rhport].ep_fifo_size - _allocated_fifo_words_tx * 4);
|
||||
|
||||
// DIEPTXF starts at FIFO #1.
|
||||
// Both TXFD and TXSA are in unit of 32-bit words.
|
||||
dwc2->dieptxf[epnum - 1] = (fifo_size << DIEPTXF_INEPTXFD_Pos) |
|
||||
(_dwc2_controller[rhport].ep_fifo_size / 4 - _allocated_fifo_words_tx);
|
||||
|
||||
dwc2->epin[epnum].diepctl |= (1 << DIEPCTL_USBAEP_Pos) |
|
||||
(epnum << DIEPCTL_TXFNUM_Pos) |
|
||||
(desc_edpt->bmAttributes.xfer << DIEPCTL_EPTYP_Pos) |
|
||||
(desc_edpt->bmAttributes.xfer != TUSB_XFER_ISOCHRONOUS ? DIEPCTL_SD0PID_SEVNFRM : 0) |
|
||||
(xfer->max_size << DIEPCTL_MPSIZ_Pos);
|
||||
|
||||
dwc2->daintmsk |= (1 << (DAINTMSK_IEPM_Pos + epnum));
|
||||
}
|
||||
|
||||
TU_ASSERT(fifo_alloc(rhport, desc_edpt->bEndpointAddress, tu_edpt_packet_size(desc_edpt)));
|
||||
edpt_activate(rhport, desc_edpt);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -638,16 +711,39 @@ void dcd_edpt_close_all(uint8_t rhport) {
|
||||
|
||||
for (uint8_t n = 1; n < ep_count; n++) {
|
||||
// disable OUT endpoint
|
||||
dwc2->epout[n].doepctl = 0;
|
||||
if (dwc2->epout[n].doepctl & DOEPCTL_EPENA) {
|
||||
dwc2->epout[n].doepctl |= DOEPCTL_SNAK | DOEPCTL_EPDIS;
|
||||
}
|
||||
xfer_status[n][TUSB_DIR_OUT].max_size = 0;
|
||||
|
||||
// disable IN endpoint
|
||||
dwc2->epin[n].diepctl = 0;
|
||||
if (dwc2->epin[n].diepctl & DIEPCTL_EPENA) {
|
||||
dwc2->epin[n].diepctl |= DIEPCTL_SNAK | DIEPCTL_EPDIS;
|
||||
}
|
||||
xfer_status[n][TUSB_DIR_IN].max_size = 0;
|
||||
}
|
||||
|
||||
// reset allocated fifo OUT
|
||||
dwc2->grxfsiz = calc_grxfsiz(64, ep_count);
|
||||
// reset allocated fifo IN
|
||||
_allocated_fifo_words_tx = 16;
|
||||
|
||||
fifo_flush_tx(dwc2, 0x10); // all tx fifo
|
||||
fifo_flush_rx(dwc2);
|
||||
}
|
||||
|
||||
bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size) {
|
||||
TU_ASSERT(fifo_alloc(rhport, ep_addr, largest_packet_size));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool dcd_edpt_iso_activate(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc) {
|
||||
// Disable EP to clear potential incomplete transfers
|
||||
edpt_disable(rhport, p_endpoint_desc->bEndpointAddress, false);
|
||||
|
||||
edpt_activate(rhport, p_endpoint_desc);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes) {
|
||||
@@ -707,89 +803,12 @@ bool dcd_edpt_xfer_fifo(uint8_t rhport, uint8_t ep_addr, tu_fifo_t* ff, uint16_t
|
||||
return true;
|
||||
}
|
||||
|
||||
static void dcd_edpt_disable(uint8_t rhport, uint8_t ep_addr, bool stall) {
|
||||
(void) rhport;
|
||||
|
||||
dwc2_regs_t* dwc2 = DWC2_REG(rhport);
|
||||
|
||||
uint8_t const epnum = tu_edpt_number(ep_addr);
|
||||
uint8_t const dir = tu_edpt_dir(ep_addr);
|
||||
|
||||
if (dir == TUSB_DIR_IN) {
|
||||
dwc2_epin_t* epin = dwc2->epin;
|
||||
|
||||
// Only disable currently enabled non-control endpoint
|
||||
if ((epnum == 0) || !(epin[epnum].diepctl & DIEPCTL_EPENA)) {
|
||||
epin[epnum].diepctl |= DIEPCTL_SNAK | (stall ? DIEPCTL_STALL : 0);
|
||||
} else {
|
||||
// Stop transmitting packets and NAK IN xfers.
|
||||
epin[epnum].diepctl |= DIEPCTL_SNAK;
|
||||
while ((epin[epnum].diepint & DIEPINT_INEPNE) == 0) {}
|
||||
|
||||
// Disable the endpoint.
|
||||
epin[epnum].diepctl |= DIEPCTL_EPDIS | (stall ? DIEPCTL_STALL : 0);
|
||||
while ((epin[epnum].diepint & DIEPINT_EPDISD_Msk) == 0) {}
|
||||
|
||||
epin[epnum].diepint = DIEPINT_EPDISD;
|
||||
}
|
||||
|
||||
// Flush the FIFO, and wait until we have confirmed it cleared.
|
||||
dwc2->grstctl = ((epnum << GRSTCTL_TXFNUM_Pos) | GRSTCTL_TXFFLSH);
|
||||
while ((dwc2->grstctl & GRSTCTL_TXFFLSH_Msk) != 0) {}
|
||||
} else {
|
||||
dwc2_epout_t* epout = dwc2->epout;
|
||||
|
||||
// Only disable currently enabled non-control endpoint
|
||||
if ((epnum == 0) || !(epout[epnum].doepctl & DOEPCTL_EPENA)) {
|
||||
epout[epnum].doepctl |= stall ? DOEPCTL_STALL : 0;
|
||||
} else {
|
||||
// Asserting GONAK is required to STALL an OUT endpoint.
|
||||
// Simpler to use polling here, we don't use the "B"OUTNAKEFF interrupt
|
||||
// anyway, and it can't be cleared by user code. If this while loop never
|
||||
// finishes, we have bigger problems than just the stack.
|
||||
dwc2->dctl |= DCTL_SGONAK;
|
||||
while ((dwc2->gintsts & GINTSTS_BOUTNAKEFF_Msk) == 0) {}
|
||||
|
||||
// Ditto here- disable the endpoint.
|
||||
epout[epnum].doepctl |= DOEPCTL_EPDIS | (stall ? DOEPCTL_STALL : 0);
|
||||
while ((epout[epnum].doepint & DOEPINT_EPDISD_Msk) == 0) {}
|
||||
|
||||
epout[epnum].doepint = DOEPINT_EPDISD;
|
||||
|
||||
// Allow other OUT endpoints to keep receiving.
|
||||
dwc2->dctl |= DCTL_CGONAK;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Close an endpoint.
|
||||
*/
|
||||
void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr) {
|
||||
dwc2_regs_t* dwc2 = DWC2_REG(rhport);
|
||||
|
||||
uint8_t const epnum = tu_edpt_number(ep_addr);
|
||||
uint8_t const dir = tu_edpt_dir(ep_addr);
|
||||
|
||||
dcd_edpt_disable(rhport, ep_addr, false);
|
||||
|
||||
// Update max_size
|
||||
xfer_status[epnum][dir].max_size = 0; // max_size = 0 marks a disabled EP - required for changing FIFO allocation
|
||||
|
||||
if (dir == TUSB_DIR_IN) {
|
||||
uint16_t const fifo_size = (dwc2->dieptxf[epnum - 1] & DIEPTXF_INEPTXFD_Msk) >> DIEPTXF_INEPTXFD_Pos;
|
||||
uint16_t const fifo_start = (dwc2->dieptxf[epnum - 1] & DIEPTXF_INEPTXSA_Msk) >> DIEPTXF_INEPTXSA_Pos;
|
||||
|
||||
// For now only the last opened endpoint can be closed without fuss.
|
||||
TU_ASSERT(fifo_start == _dwc2_controller[rhport].ep_fifo_size / 4 - _allocated_fifo_words_tx,);
|
||||
_allocated_fifo_words_tx -= fifo_size;
|
||||
} else {
|
||||
_out_ep_closed = true; // Set flag such that RX FIFO gets reduced in size once RX FIFO is empty
|
||||
}
|
||||
edpt_disable(rhport, ep_addr, false);
|
||||
}
|
||||
|
||||
void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) {
|
||||
dcd_edpt_disable(rhport, ep_addr, true);
|
||||
edpt_disable(rhport, ep_addr, true);
|
||||
}
|
||||
|
||||
void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) {
|
||||
@@ -1130,18 +1149,16 @@ void dcd_int_handler(uint8_t rhport) {
|
||||
dwc2->gotgint = otg_int;
|
||||
}
|
||||
|
||||
if (int_status & GINTSTS_SOF) {
|
||||
dwc2->gotgint = GINTSTS_SOF;
|
||||
if(int_status & GINTSTS_SOF) {
|
||||
dwc2->gintsts = GINTSTS_SOF;
|
||||
const uint32_t frame = (dwc2->dsts & DSTS_FNSOF) >> DSTS_FNSOF_Pos;
|
||||
|
||||
if (_sof_en) {
|
||||
uint32_t frame = (dwc2->dsts & (DSTS_FNSOF)) >> 8;
|
||||
dcd_event_sof(rhport, frame, true);
|
||||
} else {
|
||||
// Disable SOF interrupt if SOF was not explicitly enabled. SOF was used for remote wakeup detection
|
||||
// Disable SOF interrupt if SOF was not explicitly enabled since SOF was used for remote wakeup detection
|
||||
if (!_sof_en) {
|
||||
dwc2->gintmsk &= ~GINTMSK_SOFM;
|
||||
}
|
||||
|
||||
dcd_event_bus_signal(rhport, DCD_EVENT_SOF, true);
|
||||
dcd_event_sof(rhport, frame, true);
|
||||
}
|
||||
|
||||
// RxFIFO non-empty interrupt handling.
|
||||
@@ -1154,15 +1171,7 @@ void dcd_int_handler(uint8_t rhport) {
|
||||
// Loop until all available packets were handled
|
||||
do {
|
||||
handle_rxflvl_irq(rhport);
|
||||
} while (dwc2->gotgint & GINTSTS_RXFLVL);
|
||||
|
||||
// Manage RX FIFO size
|
||||
if (_out_ep_closed) {
|
||||
update_grxfsiz(rhport);
|
||||
|
||||
// Disable flag
|
||||
_out_ep_closed = false;
|
||||
}
|
||||
} while(dwc2->gintsts & GINTSTS_RXFLVL);
|
||||
|
||||
dwc2->gintmsk |= GINTMSK_RXFLVLM;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user