Merge pull request #518 from kasjer/kasjer/nrf5x-iso-support

nrf5x iso support
This commit is contained in:
Ha Thach 2020-10-02 21:17:24 +07:00 committed by GitHub
commit 9c4aceaaa8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 154 additions and 23 deletions

View File

@ -37,6 +37,10 @@
#define CFG_TUD_TASK_QUEUE_SZ 16
#endif
#ifndef CFG_TUD_EP_MAX
#define CFG_TUD_EP_MAX 9
#endif
//--------------------------------------------------------------------+
// Device Data
//--------------------------------------------------------------------+
@ -57,7 +61,7 @@ typedef struct
uint8_t speed;
uint8_t itf2drv[16]; // map interface number to driver (0xff is invalid)
uint8_t ep2drv[8][2]; // map endpoint to driver ( 0xff is invalid )
uint8_t ep2drv[CFG_TUD_EP_MAX][2]; // map endpoint to driver ( 0xff is invalid )
struct TU_ATTR_PACKED
{
@ -66,7 +70,7 @@ typedef struct
volatile bool claimed : 1;
// TODO merge ep2drv here, 4-bit should be sufficient
}ep_status[8][2];
}ep_status[CFG_TUD_EP_MAX][2];
}usbd_device_t;
@ -249,7 +253,7 @@ static osal_mutex_t _usbd_mutex;
//--------------------------------------------------------------------+
// Prototypes
//--------------------------------------------------------------------+
static void mark_interface_endpoint(uint8_t ep2drv[8][2], uint8_t const* p_desc, uint16_t desc_len, uint8_t driver_id);
static void mark_interface_endpoint(uint8_t ep2drv[][2], uint8_t const* p_desc, uint16_t desc_len, uint8_t driver_id);
static bool process_control_request(uint8_t rhport, tusb_control_request_t const * p_request);
static bool process_set_config(uint8_t rhport, uint8_t cfg_num);
static bool process_get_descriptor(uint8_t rhport, tusb_control_request_t const * p_request);
@ -842,7 +846,7 @@ static bool process_set_config(uint8_t rhport, uint8_t cfg_num)
}
// Helper marking endpoint of interface belongs to class driver
static void mark_interface_endpoint(uint8_t ep2drv[8][2], uint8_t const* p_desc, uint16_t desc_len, uint8_t driver_id)
static void mark_interface_endpoint(uint8_t ep2drv[][2], uint8_t const* p_desc, uint16_t desc_len, uint8_t driver_id)
{
uint16_t len = 0;

View File

@ -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
};
@ -62,11 +65,14 @@ typedef struct
uint8_t* buffer;
uint16_t total_len;
volatile uint16_t actual_len;
uint8_t mps; // max packet size
uint16_t mps; // max packet size
// 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;
}
@ -205,7 +230,7 @@ static void xact_in_prepare(uint8_t epnum)
xfer_td_t* xfer = get_td(epnum, TUSB_DIR_IN);
// Each transaction is up to Max Packet Size
uint8_t const xact_len = tu_min16(xfer->total_len - xfer->actual_len, xfer->mps);
uint16_t const xact_len = tu_min16(xfer->total_len - xfer->actual_len, xfer->mps);
NRF_USBD->EPIN[epnum].PTR = (uint32_t) xfer->buffer;
NRF_USBD->EPIN[epnum].MAXCNT = xact_len;
@ -296,20 +321,94 @@ 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();
return true;
}
void dcd_edpt_close (uint8_t rhport, uint8_t ep_addr)
{
(void) rhport;
uint8_t const epnum = tu_edpt_number(ep_addr);
uint8_t const dir = tu_edpt_dir(ep_addr);
if (epnum != EP_ISO_NUM)
{
// 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
{
_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();
}
bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes)
{
(void) rhport;
@ -361,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;
}
@ -376,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;
@ -435,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);
}
@ -518,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))
{
@ -530,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);