enhance and fix bug with dcd for nrf5x

fix issue with large enough bulk out.
This commit is contained in:
hathach 2018-06-23 13:20:07 +07:00
parent b9f8575e2d
commit 84ef486418
1 changed files with 85 additions and 75 deletions

View File

@ -47,6 +47,7 @@
#include "nrf_drv_usbd_errata.h" #include "nrf_drv_usbd_errata.h"
#include "device/dcd.h" #include "device/dcd.h"
#include "device/usbd_pvt.h" // to use defer function helper
/*------------------------------------------------------------------*/ /*------------------------------------------------------------------*/
/* MACRO TYPEDEF CONSTANT ENUM /* MACRO TYPEDEF CONSTANT ENUM
@ -70,8 +71,8 @@ typedef struct
uint16_t actual_len; uint16_t actual_len;
uint8_t mps; // max packet size uint8_t mps; // max packet size
// FIXME nrf52840 auto ACK OUT packet after DMA is done // nrf52840 will auto ACK OUT packet after DMA is done
bool data_received; volatile bool data_received; // indicate packet is already ACK
} nom_xfer_t; } nom_xfer_t;
@ -140,29 +141,34 @@ void dcd_set_config (uint8_t rhport, uint8_t config_num)
/*------------------------------------------------------------------*/ /*------------------------------------------------------------------*/
/* Control /* Control
*------------------------------------------------------------------*/ *------------------------------------------------------------------*/
static void edpt_dma_start(uint8_t epnum, uint8_t dir) static void edpt_dma_start(volatile uint32_t* reg_startep)
{ {
// Only one dma could be active, TODO resolve when this is called in ISR and dma is running // Only one dma can be active
while ( _dcd.dma_running ) if ( _dcd.dma_running )
{ {
TU_ASSERT ( 0 == (SCB->ICSR & SCB_ICSR_VECTACTIVE_Msk), ); if (SCB->ICSR & SCB_ICSR_VECTACTIVE_Msk)
{
// If called within ISR, use usbd task to defer later
usbd_defer_func( (osal_task_func_t) edpt_dma_start, (void*) reg_startep, true );
return;
}
else
{
// Otherwise simply block wait
while ( _dcd.dma_running ) { }
}
} }
_dcd.dma_running = true; _dcd.dma_running = true;
if ( dir == TUSB_DIR_OUT ) (*reg_startep) = 1;
{
NRF_USBD->TASKS_STARTEPOUT[epnum] = 1;
} else
{
NRF_USBD->TASKS_STARTEPIN[epnum] = 1;
}
__ISB(); __DSB(); __ISB(); __DSB();
} }
static void edpt_dma_end(void) static void edpt_dma_end(void)
{ {
TU_ASSERT(_dcd.dma_running, );
_dcd.dma_running = false; _dcd.dma_running = false;
} }
@ -184,7 +190,7 @@ static void xact_control_start(void)
NRF_USBD->EPIN[0].PTR = (uint32_t) _dcd.control.buffer; NRF_USBD->EPIN[0].PTR = (uint32_t) _dcd.control.buffer;
NRF_USBD->EPIN[0].MAXCNT = xact_len; NRF_USBD->EPIN[0].MAXCNT = xact_len;
edpt_dma_start(0, TUSB_DIR_IN); edpt_dma_start(&NRF_USBD->TASKS_STARTEPIN[0]);
} }
_dcd.control.buffer += xact_len; _dcd.control.buffer += xact_len;
@ -231,7 +237,8 @@ static inline nom_xfer_t* get_td(uint8_t epnum, uint8_t dir)
*/ */
static void xact_out_prepare(uint8_t epnum) static void xact_out_prepare(uint8_t epnum)
{ {
// Write any value to size will allow hw to ACK (accept data) // Write zero value to SIZE register will allow hw to ACK (accept data)
// If it is not already done by DMA
NRF_USBD->SIZE.EPOUT[epnum] = 0; NRF_USBD->SIZE.EPOUT[epnum] = 0;
__ISB(); __DSB(); __ISB(); __DSB();
} }
@ -246,7 +253,7 @@ static void xact_out_dma(uint8_t epnum)
NRF_USBD->EPOUT[epnum].PTR = (uint32_t) xfer->buffer; NRF_USBD->EPOUT[epnum].PTR = (uint32_t) xfer->buffer;
NRF_USBD->EPOUT[epnum].MAXCNT = xact_len; NRF_USBD->EPOUT[epnum].MAXCNT = xact_len;
edpt_dma_start(epnum, TUSB_DIR_OUT); edpt_dma_start(&NRF_USBD->TASKS_STARTEPOUT[epnum]);
xfer->buffer += xact_len; xfer->buffer += xact_len;
xfer->actual_len += xact_len; xfer->actual_len += xact_len;
@ -271,7 +278,7 @@ static void xact_in_prepare(uint8_t epnum)
xfer->buffer += xact_len; xfer->buffer += xact_len;
edpt_dma_start(epnum, TUSB_DIR_IN); edpt_dma_start(&NRF_USBD->TASKS_STARTEPIN[epnum]);
} }
bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt) bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt)
@ -314,10 +321,8 @@ bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t
{ {
if ( xfer->data_received ) if ( xfer->data_received )
{ {
xfer->data_received = false; // nrf52840 auto ACK OUT packet after DMA is done
// Data already received previously --> trigger DMA to copy to SRAM
// FIXME nrf52840 auto ACK OUT packet after DMA is done
// Data already received preivously
xact_out_dma(epnum); xact_out_dma(epnum);
}else }else
{ {
@ -421,12 +426,12 @@ void USBD_IRQHandler(void)
{ {
if ( _dcd.control.dir == TUSB_DIR_OUT ) if ( _dcd.control.dir == TUSB_DIR_OUT )
{ {
// OUT data from Host -> Endpoint // Control OUT: data from Host -> Endpoint
// Trigger DMA to move Endpoint -> SRAM // Trigger DMA to move Endpoint -> SRAM
edpt_dma_start(0, TUSB_DIR_OUT); edpt_dma_start(&NRF_USBD->TASKS_STARTEPOUT[0]);
}else }else
{ {
// IN: data transferred from Endpoint -> Host // Control IN: data transferred from Endpoint -> Host
if ( _dcd.control.actual_len < _dcd.control.total_len ) if ( _dcd.control.actual_len < _dcd.control.total_len )
{ {
xact_control_start(); xact_control_start();
@ -438,7 +443,7 @@ void USBD_IRQHandler(void)
} }
} }
// OUT data moved from Endpoint -> SRAM // Control OUT: data from Endpoint -> SRAM
if ( int_status & USBD_INTEN_ENDEPOUT0_Msk) if ( int_status & USBD_INTEN_ENDEPOUT0_Msk)
{ {
if ( _dcd.control.actual_len < _dcd.control.total_len ) if ( _dcd.control.actual_len < _dcd.control.total_len )
@ -452,55 +457,12 @@ void USBD_IRQHandler(void)
} }
/*------------- Bulk/Interrupt Transfer -------------*/ /*------------- Bulk/Interrupt Transfer -------------*/
if ( int_status & USBD_INTEN_EPDATA_Msk)
{
uint32_t data_status = NRF_USBD->EPDATASTATUS;
nrf_usbd_epdatastatus_clear(data_status); /* Bulk/Int OUT: data from DMA -> SRAM
* Note: Since nrf controller auto ACK next packet without SW awareness
// In: data from Endpoint -> Host * We must handle this stage before Host -> Endpoint just in case
for(uint8_t epnum=1; epnum<8; epnum++) * 2 event happens at once
{ */
if ( BIT_TEST_(data_status, epnum ) )
{
nom_xfer_t* xfer = get_td(epnum, TUSB_DIR_IN);
xfer->actual_len += NRF_USBD->EPIN[epnum].MAXCNT;
if ( xfer->actual_len < xfer->total_len )
{
// more to xfer
xact_in_prepare(epnum);
} else
{
// BULK/INT IN complete
dcd_xfer_complete(0, epnum | TUSB_DIR_IN_MASK, xfer->actual_len, true);
}
}
}
// OUT: data from Host -> Endpoint
for(uint8_t epnum=1; epnum<8; epnum++)
{
if ( BIT_TEST_(data_status, 16+epnum ) )
{
nom_xfer_t* xfer = get_td(epnum, TUSB_DIR_OUT);
if (xfer->actual_len < xfer->total_len)
{
xact_out_dma(epnum);
}else
{
// FIXME nrf52840 auto ACK OUT packet after DMA is done
// Mark this endpoint with data received
xfer->data_received = true;
}
}
}
}
// OUT: data from DMA -> SRAM
for(uint8_t epnum=1; epnum<8; epnum++) for(uint8_t epnum=1; epnum<8; epnum++)
{ {
if ( BIT_TEST_(int_status, USBD_INTEN_ENDEPOUT0_Pos+epnum) ) if ( BIT_TEST_(int_status, USBD_INTEN_ENDEPOUT0_Pos+epnum) )
@ -509,10 +471,12 @@ void USBD_IRQHandler(void)
uint8_t const xact_len = NRF_USBD->EPOUT[epnum].AMOUNT; uint8_t const xact_len = NRF_USBD->EPOUT[epnum].AMOUNT;
xfer->data_received = false;
// Transfer complete if transaction len < Max Packet Size or total len is transferred // Transfer complete if transaction len < Max Packet Size or total len is transferred
if ( (xact_len == xfer->mps) && (xfer->actual_len < xfer->total_len) ) if ( (xact_len == xfer->mps) && (xfer->actual_len < xfer->total_len) )
{ {
// Prepare for more data from Host -> Endpoint // Prepare for next transaction
xact_out_prepare(epnum); xact_out_prepare(epnum);
}else }else
{ {
@ -524,6 +488,52 @@ void USBD_IRQHandler(void)
} }
} }
if ( int_status & USBD_INTEN_EPDATA_Msk)
{
uint32_t data_status = NRF_USBD->EPDATASTATUS;
nrf_usbd_epdatastatus_clear(data_status);
// Bulk/Int In: data from Endpoint -> Host
for(uint8_t epnum=1; epnum<8; epnum++)
{
if ( BIT_TEST_(data_status, epnum ) )
{
nom_xfer_t* xfer = get_td(epnum, TUSB_DIR_IN);
xfer->actual_len += NRF_USBD->EPIN[epnum].MAXCNT;
if ( xfer->actual_len < xfer->total_len )
{
// prepare next transaction
xact_in_prepare(epnum);
} else
{
// Bulk/Int IN complete
dcd_xfer_complete(0, epnum | TUSB_DIR_IN_MASK, xfer->actual_len, true);
}
}
}
// Bulk/Int OUT: data from Host -> Endpoint
for(uint8_t epnum=1; epnum<8; epnum++)
{
if ( BIT_TEST_(data_status, 16+epnum ) )
{
nom_xfer_t* xfer = get_td(epnum, TUSB_DIR_OUT);
if (xfer->actual_len < xfer->total_len)
{
xact_out_dma(epnum);
}else
{
// Data overflow !!! Nah, nrf52840 will auto ACK OUT packet after DMA is done
// Mark this endpoint with data received
xfer->data_received = true;
}
}
}
}
// SOF interrupt // SOF interrupt
if ( int_status & USBD_INTEN_SOF_Msk ) if ( int_status & USBD_INTEN_SOF_Msk )