mirror of
https://github.com/hathach/tinyusb.git
synced 2025-01-17 05:32:55 +08:00
Merge pull request #518 from kasjer/kasjer/nrf5x-iso-support
nrf5x iso support
This commit is contained in:
commit
9c4aceaaa8
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user