mirror of
https://github.com/hathach/tinyusb.git
synced 2025-03-18 16:21:18 +00:00
nrf5x: Add support for ISO endpoints
ISO endpoints were not covered so far by the driver code. This adds support for ISO IN and OUT endpoint handling. Registers for ISO IN(OUT) endpoints are placed just after normal IN(OUT) so in some cases common code could be used for handling all type of transfers. Generally code synchronizes ISO endpoint handling to SOF interrupt. This code does not change the way of how non-ISO endpoints are treated. Code uses strategy outlined in nRF52840 Produce Specification v1.0 sections 6.35.11.1 and 6.35.11.2.
This commit is contained in:
parent
6f5ee09511
commit
96da1ca4b8
@ -53,6 +53,9 @@ enum
|
||||
|
||||
enum
|
||||
{
|
||||
// Endpoint number is fixed (8) for ISOOUT and ISOIN.
|
||||
EP_ISO_NUM = 8,
|
||||
// CBI endpoints count
|
||||
EP_COUNT = 8
|
||||
};
|
||||
|
||||
@ -67,6 +70,9 @@ typedef struct
|
||||
// nrf52840 will auto ACK OUT packet after DMA is done
|
||||
// indicate packet is already ACK
|
||||
volatile bool data_received;
|
||||
// Set to true when data was transferred from RAM to ISO IN output buffer.
|
||||
// New data can be put in ISO IN output buffer after SOF.
|
||||
bool iso_in_transfer_ready;
|
||||
|
||||
} xfer_td_t;
|
||||
|
||||
@ -74,7 +80,8 @@ typedef struct
|
||||
static struct
|
||||
{
|
||||
// All 8 endpoints including control IN & OUT (offset 1)
|
||||
xfer_td_t xfer[EP_COUNT][2];
|
||||
// +1 for ISO endpoints
|
||||
xfer_td_t xfer[EP_COUNT + 1][2];
|
||||
|
||||
// Number of pending DMA that is started but not handled yet by dcd_int_handler().
|
||||
// Since nRF can only carry one DMA can run at a time, this value is normally be either 0 or 1.
|
||||
@ -173,6 +180,7 @@ static void xact_out_prepare(uint8_t epnum)
|
||||
{
|
||||
// Write zero value to SIZE register will allow hw to ACK (accept data)
|
||||
// If it is not already done by DMA
|
||||
// SIZE.ISOOUT can also be accessed this way
|
||||
NRF_USBD->SIZE.EPOUT[epnum] = 0;
|
||||
}
|
||||
|
||||
@ -183,15 +191,32 @@ static void xact_out_prepare(uint8_t epnum)
|
||||
static void xact_out_dma(uint8_t epnum)
|
||||
{
|
||||
xfer_td_t* xfer = get_td(epnum, TUSB_DIR_OUT);
|
||||
uint32_t xact_len;
|
||||
|
||||
uint8_t const xact_len = NRF_USBD->SIZE.EPOUT[epnum];
|
||||
if (epnum == EP_ISO_NUM)
|
||||
{
|
||||
xact_len = NRF_USBD->SIZE.ISOOUT;
|
||||
// If ZERO bit is set, ignore ISOOUT length
|
||||
if (xact_len & USBD_SIZE_ISOOUT_ZERO_Msk) xact_len = 0;
|
||||
else
|
||||
{
|
||||
// Trigger DMA move data from Endpoint -> SRAM
|
||||
NRF_USBD->ISOOUT.PTR = (uint32_t) xfer->buffer;
|
||||
NRF_USBD->ISOOUT.MAXCNT = xact_len;
|
||||
|
||||
// Trigger DMA move data from Endpoint -> SRAM
|
||||
NRF_USBD->EPOUT[epnum].PTR = (uint32_t) xfer->buffer;
|
||||
NRF_USBD->EPOUT[epnum].MAXCNT = xact_len;
|
||||
edpt_dma_start(&NRF_USBD->TASKS_STARTISOOUT);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
xact_len = (uint8_t)NRF_USBD->SIZE.EPOUT[epnum];
|
||||
|
||||
edpt_dma_start(&NRF_USBD->TASKS_STARTEPOUT[epnum]);
|
||||
// Trigger DMA move data from Endpoint -> SRAM
|
||||
NRF_USBD->EPOUT[epnum].PTR = (uint32_t) xfer->buffer;
|
||||
NRF_USBD->EPOUT[epnum].MAXCNT = xact_len;
|
||||
|
||||
edpt_dma_start(&NRF_USBD->TASKS_STARTEPOUT[epnum]);
|
||||
}
|
||||
xfer->buffer += xact_len;
|
||||
xfer->actual_len += xact_len;
|
||||
}
|
||||
@ -296,14 +321,44 @@ bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt)
|
||||
|
||||
_dcd.xfer[epnum][dir].mps = desc_edpt->wMaxPacketSize.size;
|
||||
|
||||
if ( dir == TUSB_DIR_OUT )
|
||||
if (desc_edpt->bmAttributes.xfer != TUSB_XFER_ISOCHRONOUS)
|
||||
{
|
||||
NRF_USBD->INTENSET = TU_BIT(USBD_INTEN_ENDEPOUT0_Pos + epnum);
|
||||
NRF_USBD->EPOUTEN |= TU_BIT(epnum);
|
||||
}else
|
||||
if (dir == TUSB_DIR_OUT)
|
||||
{
|
||||
NRF_USBD->INTENSET = TU_BIT(USBD_INTEN_ENDEPOUT0_Pos + epnum);
|
||||
NRF_USBD->EPOUTEN |= TU_BIT(epnum);
|
||||
}else
|
||||
{
|
||||
NRF_USBD->INTENSET = TU_BIT(USBD_INTEN_ENDEPIN0_Pos + epnum);
|
||||
NRF_USBD->EPINEN |= TU_BIT(epnum);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
NRF_USBD->INTENSET = TU_BIT(USBD_INTEN_ENDEPIN0_Pos + epnum);
|
||||
NRF_USBD->EPINEN |= TU_BIT(epnum);
|
||||
TU_ASSERT(epnum == EP_ISO_NUM);
|
||||
if (dir == TUSB_DIR_OUT)
|
||||
{
|
||||
// SPLIT ISO buffer when ISO IN endpoint is already opened.
|
||||
if (_dcd.xfer[EP_ISO_NUM][TUSB_DIR_IN].mps) NRF_USBD->ISOSPLIT = USBD_ISOSPLIT_SPLIT_HalfIN;
|
||||
// Clear old events
|
||||
NRF_USBD->EVENTS_ENDISOOUT = 0;
|
||||
// Clear SOF event in case interrupt was not enabled yet.
|
||||
if ((NRF_USBD->INTEN & USBD_INTEN_SOF_Msk) == 0) NRF_USBD->EVENTS_SOF = 0;
|
||||
// Enable SOF and ISOOUT interrupts, and ISOOUT endpoint.
|
||||
NRF_USBD->INTENSET = USBD_INTENSET_ENDISOOUT_Msk | USBD_INTENSET_SOF_Msk;
|
||||
NRF_USBD->EPOUTEN |= USBD_EPOUTEN_ISOOUT_Msk;
|
||||
}
|
||||
else
|
||||
{
|
||||
NRF_USBD->EVENTS_ENDISOIN = 0;
|
||||
// SPLIT ISO buffer when ISO OUT endpoint is already opened.
|
||||
if (_dcd.xfer[EP_ISO_NUM][TUSB_DIR_OUT].mps) NRF_USBD->ISOSPLIT = USBD_ISOSPLIT_SPLIT_HalfIN;
|
||||
// Clear SOF event in case interrupt was not enabled yet.
|
||||
if ((NRF_USBD->INTEN & USBD_INTEN_SOF_Msk) == 0) NRF_USBD->EVENTS_SOF = 0;
|
||||
// Enable SOF and ISOIN interrupts, and ISOIN endpoint.
|
||||
NRF_USBD->INTENSET = USBD_INTENSET_ENDISOIN_Msk | USBD_INTENSET_SOF_Msk;
|
||||
NRF_USBD->EPINEN |= USBD_EPINEN_ISOIN_Msk;
|
||||
}
|
||||
}
|
||||
__ISB(); __DSB();
|
||||
|
||||
@ -317,16 +372,39 @@ void dcd_edpt_close (uint8_t rhport, uint8_t ep_addr)
|
||||
uint8_t const epnum = tu_edpt_number(ep_addr);
|
||||
uint8_t const dir = tu_edpt_dir(ep_addr);
|
||||
|
||||
// CBI
|
||||
if (dir == TUSB_DIR_OUT)
|
||||
if (epnum != EP_ISO_NUM)
|
||||
{
|
||||
NRF_USBD->INTENCLR = TU_BIT(USBD_INTEN_ENDEPOUT0_Pos + epnum);
|
||||
NRF_USBD->EPOUTEN &= ~TU_BIT(epnum);
|
||||
// CBI
|
||||
if (dir == TUSB_DIR_OUT)
|
||||
{
|
||||
NRF_USBD->INTENCLR = TU_BIT(USBD_INTEN_ENDEPOUT0_Pos + epnum);
|
||||
NRF_USBD->EPOUTEN &= ~TU_BIT(epnum);
|
||||
}
|
||||
else
|
||||
{
|
||||
NRF_USBD->INTENCLR = TU_BIT(USBD_INTEN_ENDEPIN0_Pos + epnum);
|
||||
NRF_USBD->EPINEN &= ~TU_BIT(epnum);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
NRF_USBD->INTENCLR = TU_BIT(USBD_INTEN_ENDEPIN0_Pos + epnum);
|
||||
NRF_USBD->EPINEN &= ~TU_BIT(epnum);
|
||||
_dcd.xfer[EP_ISO_NUM][dir].mps = 0;
|
||||
// ISO
|
||||
if (dir == TUSB_DIR_OUT)
|
||||
{
|
||||
NRF_USBD->INTENCLR = USBD_INTENCLR_ENDISOOUT_Msk;
|
||||
NRF_USBD->EPOUTEN &= ~USBD_EPOUTEN_ISOOUT_Msk;
|
||||
NRF_USBD->EVENTS_ENDISOOUT = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
NRF_USBD->INTENCLR = USBD_INTENCLR_ENDISOIN_Msk;
|
||||
NRF_USBD->EPINEN &= ~USBD_EPINEN_ISOIN_Msk;
|
||||
}
|
||||
// One of the ISO endpoints closed, no need to split buffers any more.
|
||||
NRF_USBD->ISOSPLIT = USBD_ISOSPLIT_SPLIT_OneDir;
|
||||
// When both ISO endpoint are close there is no need for SOF any more.
|
||||
if (_dcd.xfer[EP_ISO_NUM][TUSB_DIR_IN].mps + _dcd.xfer[EP_ISO_NUM][TUSB_DIR_OUT].mps == 0) NRF_USBD->INTENCLR = USBD_INTENCLR_SOF_Msk;
|
||||
}
|
||||
__ISB(); __DSB();
|
||||
}
|
||||
@ -382,11 +460,12 @@ bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t
|
||||
void dcd_edpt_stall (uint8_t rhport, uint8_t ep_addr)
|
||||
{
|
||||
(void) rhport;
|
||||
uint8_t const epnum = tu_edpt_number(ep_addr);
|
||||
|
||||
if ( tu_edpt_number(ep_addr) == 0 )
|
||||
if ( epnum == 0 )
|
||||
{
|
||||
NRF_USBD->TASKS_EP0STALL = 1;
|
||||
}else
|
||||
}else if (epnum != EP_ISO_NUM)
|
||||
{
|
||||
NRF_USBD->EPSTALL = (USBD_EPSTALL_STALL_Stall << USBD_EPSTALL_STALL_Pos) | ep_addr;
|
||||
}
|
||||
@ -397,8 +476,9 @@ void dcd_edpt_stall (uint8_t rhport, uint8_t ep_addr)
|
||||
void dcd_edpt_clear_stall (uint8_t rhport, uint8_t ep_addr)
|
||||
{
|
||||
(void) rhport;
|
||||
uint8_t const epnum = tu_edpt_number(ep_addr);
|
||||
|
||||
if ( tu_edpt_number(ep_addr) )
|
||||
if ( epnum != 0 && epnum != EP_ISO_NUM )
|
||||
{
|
||||
// clear stall
|
||||
NRF_USBD->EPSTALL = (USBD_EPSTALL_STALL_UnStall << USBD_EPSTALL_STALL_Pos) | ep_addr;
|
||||
@ -456,8 +536,31 @@ void dcd_int_handler(uint8_t rhport)
|
||||
dcd_event_bus_signal(0, DCD_EVENT_BUS_RESET, true);
|
||||
}
|
||||
|
||||
// ISOIN: Data was moved to endpoint buffer, client will be notified in SOF
|
||||
if ( int_status & USBD_INTEN_ENDISOIN_Msk )
|
||||
{
|
||||
xfer_td_t* xfer = get_td(EP_ISO_NUM, TUSB_DIR_IN);
|
||||
|
||||
xfer->actual_len = NRF_USBD->ISOIN.AMOUNT;
|
||||
// Data transferred from RAM to endpoint output buffer.
|
||||
// Next transfer can be scheduled after SOF.
|
||||
xfer->iso_in_transfer_ready = true;
|
||||
}
|
||||
|
||||
if ( int_status & USBD_INTEN_SOF_Msk )
|
||||
{
|
||||
// ISOOUT: Transfer data gathered in previous frame from buffer to RAM
|
||||
if (NRF_USBD->EPOUTEN & USBD_EPOUTEN_ISOOUT_Msk)
|
||||
{
|
||||
xact_out_dma(EP_ISO_NUM);
|
||||
}
|
||||
// ISOIN: Notify client that data was transferred
|
||||
xfer_td_t* xfer = get_td(EP_ISO_NUM, TUSB_DIR_IN);
|
||||
if ( xfer->iso_in_transfer_ready )
|
||||
{
|
||||
xfer->iso_in_transfer_ready = false;
|
||||
dcd_event_xfer_complete(0, EP_ISO_NUM | TUSB_DIR_IN_MASK, xfer->actual_len, XFER_RESULT_SUCCESS, true);
|
||||
}
|
||||
dcd_event_bus_signal(0, DCD_EVENT_SOF, true);
|
||||
}
|
||||
|
||||
@ -539,8 +642,11 @@ void dcd_int_handler(uint8_t rhport)
|
||||
* Note: Since nRF controller auto ACK next packet without SW awareness
|
||||
* We must handle this stage before Host -> Endpoint just in case
|
||||
* 2 event happens at once
|
||||
* ISO OUT: Transaction must fit in single packed, it can be shorter then total
|
||||
* len if Host decides to sent fewer bytes, it this case transaction is also
|
||||
* complete and next transfer is not initiated here like for CBI.
|
||||
*/
|
||||
for(uint8_t epnum=0; epnum<8; epnum++)
|
||||
for(uint8_t epnum=0; epnum<EP_COUNT+1; epnum++)
|
||||
{
|
||||
if ( tu_bit_test(int_status, USBD_INTEN_ENDEPOUT0_Pos+epnum))
|
||||
{
|
||||
@ -551,7 +657,7 @@ void dcd_int_handler(uint8_t rhport)
|
||||
xfer->data_received = false;
|
||||
|
||||
// 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 ( (epnum != EP_ISO_NUM) && (xact_len == xfer->mps) && (xfer->actual_len < xfer->total_len) )
|
||||
{
|
||||
// Prepare for next transaction
|
||||
xact_out_prepare(epnum);
|
||||
|
Loading…
x
Reference in New Issue
Block a user