summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/device/usbd.c12
-rw-r--r--src/portable/nordic/nrf5x/dcd_nrf5x.c165
2 files changed, 154 insertions, 23 deletions
diff --git a/src/device/usbd.c b/src/device/usbd.c
index d1e984a0e..3a6ce9b6c 100644
--- a/src/device/usbd.c
+++ b/src/device/usbd.c
@@ -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;
diff --git a/src/portable/nordic/nrf5x/dcd_nrf5x.c b/src/portable/nordic/nrf5x/dcd_nrf5x.c
index 60da9fb25..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
};
@@ -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);