Răsfoiți Sursa

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

nrf5x iso support
Ha Thach 5 ani în urmă
părinte
comite
9c4aceaaa8
2 a modificat fișierele cu 154 adăugiri și 23 ștergeri
  1. 8 4
      src/device/usbd.c
  2. 146 19
      src/portable/nordic/nrf5x/dcd_nrf5x.c

+ 8 - 4
src/device/usbd.c

@@ -37,6 +37,10 @@
 #define CFG_TUD_TASK_QUEUE_SZ   16
 #define CFG_TUD_TASK_QUEUE_SZ   16
 #endif
 #endif
 
 
+#ifndef CFG_TUD_EP_MAX
+#define CFG_TUD_EP_MAX          9
+#endif
+
 //--------------------------------------------------------------------+
 //--------------------------------------------------------------------+
 // Device Data
 // Device Data
 //--------------------------------------------------------------------+
 //--------------------------------------------------------------------+
@@ -57,7 +61,7 @@ typedef struct
   uint8_t speed;
   uint8_t speed;
 
 
   uint8_t itf2drv[16];     // map interface number to driver (0xff is invalid)
   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
   struct TU_ATTR_PACKED
   {
   {
@@ -66,7 +70,7 @@ typedef struct
     volatile bool claimed : 1;
     volatile bool claimed : 1;
 
 
     // TODO merge ep2drv here, 4-bit should be sufficient
     // TODO merge ep2drv here, 4-bit should be sufficient
-  }ep_status[8][2];
+  }ep_status[CFG_TUD_EP_MAX][2];
 
 
 }usbd_device_t;
 }usbd_device_t;
 
 
@@ -249,7 +253,7 @@ static osal_mutex_t _usbd_mutex;
 //--------------------------------------------------------------------+
 //--------------------------------------------------------------------+
 // Prototypes
 // 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_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_set_config(uint8_t rhport, uint8_t cfg_num);
 static bool process_get_descriptor(uint8_t rhport, tusb_control_request_t const * p_request);
 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
 // 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;
   uint16_t len = 0;
 
 

+ 146 - 19
src/portable/nordic/nrf5x/dcd_nrf5x.c

@@ -53,6 +53,9 @@ enum
 
 
 enum
 enum
 {
 {
+  // Endpoint number is fixed (8) for ISOOUT and ISOIN.
+  EP_ISO_NUM = 8,
+  // CBI endpoints count
   EP_COUNT = 8
   EP_COUNT = 8
 };
 };
 
 
@@ -62,11 +65,14 @@ typedef struct
   uint8_t* buffer;
   uint8_t* buffer;
   uint16_t total_len;
   uint16_t total_len;
   volatile uint16_t actual_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
   // nrf52840 will auto ACK OUT packet after DMA is done
   // indicate packet is already ACK
   // indicate packet is already ACK
   volatile bool data_received;
   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;
 } xfer_td_t;
 
 
@@ -74,7 +80,8 @@ typedef struct
 static struct
 static struct
 {
 {
   // All 8 endpoints including control IN & OUT (offset 1)
   // 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().
   // 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.
   // 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)
     // Write zero value to SIZE register will allow hw to ACK (accept data)
     // If it is not already done by DMA
     // If it is not already done by DMA
+    // SIZE.ISOOUT can also be accessed this way
     NRF_USBD->SIZE.EPOUT[epnum] = 0;
     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)
 static void xact_out_dma(uint8_t epnum)
 {
 {
   xfer_td_t* xfer = get_td(epnum, TUSB_DIR_OUT);
   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->buffer     += xact_len;
   xfer->actual_len += 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);
   xfer_td_t* xfer = get_td(epnum, TUSB_DIR_IN);
 
 
   // Each transaction is up to Max Packet Size
   // 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].PTR    = (uint32_t) xfer->buffer;
   NRF_USBD->EPIN[epnum].MAXCNT = xact_len;
   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;
   _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();
   __ISB(); __DSB();
 
 
   return true;
   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)
 bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes)
 {
 {
   (void) rhport;
   (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 dcd_edpt_stall (uint8_t rhport, uint8_t ep_addr)
 {
 {
   (void) rhport;
   (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;
     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;
     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 dcd_edpt_clear_stall (uint8_t rhport, uint8_t ep_addr)
 {
 {
   (void) rhport;
   (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
     // clear stall
     NRF_USBD->EPSTALL = (USBD_EPSTALL_STALL_UnStall << USBD_EPSTALL_STALL_Pos) | ep_addr;
     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);
     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 )
   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);
     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
    * Note: Since nRF controller auto ACK next packet without SW awareness
    * We must handle this stage before Host -> Endpoint just in case
    * We must handle this stage before Host -> Endpoint just in case
    * 2 event happens at once
    * 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))
     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;
       xfer->data_received = false;
 
 
       // Transfer complete if transaction len < Max Packet Size or total len is transferred
       // 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
         // Prepare for next transaction
         xact_out_prepare(epnum);
         xact_out_prepare(epnum);