diff --git a/src/portable/nordic/nrf5x/dcd_nrf5x.c b/src/portable/nordic/nrf5x/dcd_nrf5x.c index a78b74d62..b85d3776f 100644 --- a/src/portable/nordic/nrf5x/dcd_nrf5x.c +++ b/src/portable/nordic/nrf5x/dcd_nrf5x.c @@ -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; epnumdata_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);