Jelajahi Sumber

feat(port/dwc2/usb_hc_dwc2): support external hs hub with fs/ls device

Signed-off-by: sakumisu <1203593632@qq.com>
sakumisu 9 bulan lalu
induk
melakukan
dc5e8dc5ed
1 mengubah file dengan 257 tambahan dan 33 penghapusan
  1. 257 33
      port/dwc2/usb_hc_dwc2.c

+ 257 - 33
port/dwc2/usb_hc_dwc2.c

@@ -42,6 +42,11 @@ struct dwc2_chan {
     uint8_t chidx;
     bool inuse;
     bool dir_in;
+    bool do_ssplit;
+    bool do_csplit;
+    uint8_t hub_addr;
+    uint8_t hub_port;
+    uint16_t ssplit_frame;
     usb_osal_sem_t waitsem;
     struct usbh_urb *urb;
     uint32_t iso_frame_idx;
@@ -216,12 +221,6 @@ static inline void dwc2_chan_char_init(struct usbh_bus *bus, uint8_t ch_num, uin
         regval |= USB_OTG_HCCHAR_EPDIR;
     }
 
-    if ((usbh_get_port_speed(bus, 0) == USB_SPEED_HIGH) && (speed != USB_SPEED_HIGH)) {
-        USB_LOG_ERR("Do not support LS/FS device on HS hub\r\n");
-        while (1) {
-        }
-    }
-
     /* LS device plugged to HUB */
     if ((speed == USB_SPEED_LOW) && (usbh_get_port_speed(bus, 0) != USB_SPEED_LOW)) {
         regval |= USB_OTG_HCCHAR_LSDEV;
@@ -234,6 +233,30 @@ static inline void dwc2_chan_char_init(struct usbh_bus *bus, uint8_t ch_num, uin
     USB_OTG_HC((uint32_t)ch_num)->HCCHAR = regval;
 }
 
+static inline void dwc2_chan_splt_init(struct usbh_bus *bus, uint8_t ch_num)
+{
+    struct dwc2_chan *chan;
+    uint32_t hcsplt;
+
+    chan = &g_dwc2_hcd[bus->hcd.hcd_id].chan_pool[ch_num];
+
+    if (chan->do_ssplit) {
+        hcsplt = USB_OTG_HCSPLT_SPLITEN;
+        hcsplt |= (chan->hub_addr << USB_OTG_HCSPLT_HUBADDR_Pos);
+        hcsplt |= chan->hub_port;
+
+        if (chan->do_csplit) {
+            hcsplt |= USB_OTG_HCSPLT_COMPLSPLT;
+        } else {
+            hcsplt &= ~USB_OTG_HCSPLT_COMPLSPLT;
+        }
+
+        USB_OTG_HC((uint32_t)ch_num)->HCSPLT = hcsplt;
+    } else {
+        USB_OTG_HC((uint32_t)ch_num)->HCSPLT = 0U;
+    }
+}
+
 static void dwc2_chan_init(struct usbh_bus *bus, uint8_t ch_num, uint8_t devaddr, uint8_t ep_addr, uint8_t ep_type, uint16_t ep_mps, uint8_t speed)
 {
     uint32_t regval;
@@ -242,18 +265,13 @@ static void dwc2_chan_init(struct usbh_bus *bus, uint8_t ch_num, uint8_t devaddr
     USB_OTG_HC((uint32_t)ch_num)->HCINT = 0xFFFFFFFFU;
 
     /* Enable channel interrupts required for this transfer. */
-    regval = USB_OTG_HCINTMSK_CHHM;
-
-    if (ep_type == USB_ENDPOINT_TYPE_INTERRUPT) {
-        regval |= USB_OTG_HCINTMSK_NAKM;
-    }
-
-    USB_OTG_HC((uint32_t)ch_num)->HCINTMSK = regval;
+    USB_OTG_HC((uint32_t)ch_num)->HCINTMSK = USB_OTG_HCINTMSK_CHHM;
 
     /* Enable the top level host channel interrupt. */
     USB_OTG_HOST->HAINTMSK |= 1UL << (ch_num & 0xFU);
 
     dwc2_chan_char_init(bus, ch_num, devaddr, ep_addr, ep_type, ep_mps, speed);
+    dwc2_chan_splt_init(bus, ch_num);
 }
 
 /* For IN channel HCTSIZ.XferSize is expected to be an integer multiple of ep_mps size.*/
@@ -352,6 +370,14 @@ static inline uint32_t dwc2_get_glb_intstatus(struct usbh_bus *bus)
     return tmpreg;
 }
 
+static inline uint16_t dwc2_get_full_frame_num(struct usbh_bus *bus)
+{
+    uint16_t frame = usbh_get_frame_number(bus);
+
+    /* USB_OTG_HFNUM_FRNUM_Msk is 0xFFFF��but max frame num is 0x3FFF */
+    return ((frame & 0x3FFF) >> 3);
+}
+
 static int dwc2_chan_alloc(struct usbh_bus *bus)
 {
     size_t flags;
@@ -362,6 +388,9 @@ static int dwc2_chan_alloc(struct usbh_bus *bus)
         if (!g_dwc2_hcd[bus->hcd.hcd_id].chan_pool[chidx].inuse) {
             g_dwc2_hcd[bus->hcd.hcd_id].chan_pool[chidx].inuse = true;
             usb_osal_leave_critical_section(flags);
+
+            g_dwc2_hcd[bus->hcd.hcd_id].chan_pool[chidx].do_ssplit = 0;
+            g_dwc2_hcd[bus->hcd.hcd_id].chan_pool[chidx].do_csplit = 0;
             return chidx;
         }
     }
@@ -404,9 +433,29 @@ static uint16_t dwc2_calculate_packet_num(uint32_t input_size, uint8_t ep_addr,
 static void dwc2_control_urb_init(struct usbh_bus *bus, uint8_t chidx, struct usbh_urb *urb, struct usb_setup_packet *setup, uint8_t *buffer, uint32_t buflen)
 {
     struct dwc2_chan *chan;
+    uint32_t datalen;
+    uint8_t data_pid;
 
     chan = &g_dwc2_hcd[bus->hcd.hcd_id].chan_pool[chidx];
 
+    /* split buflen with ep mps */
+    if (chan->do_ssplit && (chan->ep0_state == DWC2_EP0_STATE_INDATA || chan->ep0_state == DWC2_EP0_STATE_OUTDATA)) {
+        if (buflen > USB_GET_MAXPACKETSIZE(urb->ep->wMaxPacketSize)) {
+            datalen = USB_GET_MAXPACKETSIZE(urb->ep->wMaxPacketSize);
+        } else {
+            datalen = buflen;
+        }
+
+        if (urb->data_toggle == 0) {
+            data_pid = HC_PID_DATA0;
+        } else {
+            data_pid = HC_PID_DATA1;
+        }
+    } else {
+        datalen = buflen; // buflen = setup->wLength
+        data_pid = HC_PID_DATA1;
+    }
+
     if (chan->ep0_state == DWC2_EP0_STATE_SETUP) /* fill setup */
     {
         chan->num_packets = dwc2_calculate_packet_num(8, 0x00, USB_GET_MAXPACKETSIZE(urb->ep->wMaxPacketSize), &chan->xferlen);
@@ -414,14 +463,14 @@ static void dwc2_control_urb_init(struct usbh_bus *bus, uint8_t chidx, struct us
         dwc2_chan_transfer(bus, chidx, 0x00, (uint8_t *)setup, chan->xferlen, chan->num_packets, HC_PID_SETUP);
     } else if (chan->ep0_state == DWC2_EP0_STATE_INDATA) /* fill in data */
     {
-        chan->num_packets = dwc2_calculate_packet_num(setup->wLength, 0x80, USB_GET_MAXPACKETSIZE(urb->ep->wMaxPacketSize), &chan->xferlen);
+        chan->num_packets = dwc2_calculate_packet_num(datalen, 0x80, USB_GET_MAXPACKETSIZE(urb->ep->wMaxPacketSize), &chan->xferlen);
         dwc2_chan_init(bus, chidx, urb->hport->dev_addr, 0x80, USB_ENDPOINT_TYPE_CONTROL, USB_GET_MAXPACKETSIZE(urb->ep->wMaxPacketSize), urb->hport->speed);
-        dwc2_chan_transfer(bus, chidx, 0x80, buffer, chan->xferlen, chan->num_packets, HC_PID_DATA1);
+        dwc2_chan_transfer(bus, chidx, 0x80, buffer, chan->xferlen, chan->num_packets, data_pid);
     } else if (chan->ep0_state == DWC2_EP0_STATE_OUTDATA) /* fill out data */
     {
-        chan->num_packets = dwc2_calculate_packet_num(setup->wLength, 0x00, USB_GET_MAXPACKETSIZE(urb->ep->wMaxPacketSize), &chan->xferlen);
+        chan->num_packets = dwc2_calculate_packet_num(datalen, 0x00, USB_GET_MAXPACKETSIZE(urb->ep->wMaxPacketSize), &chan->xferlen);
         dwc2_chan_init(bus, chidx, urb->hport->dev_addr, 0x00, USB_ENDPOINT_TYPE_CONTROL, USB_GET_MAXPACKETSIZE(urb->ep->wMaxPacketSize), urb->hport->speed);
-        dwc2_chan_transfer(bus, chidx, 0x00, buffer, chan->xferlen, chan->num_packets, HC_PID_DATA1);
+        dwc2_chan_transfer(bus, chidx, 0x00, buffer, chan->xferlen, chan->num_packets, data_pid);
     } else if (chan->ep0_state == DWC2_EP0_STATE_INSTATUS) /* fill in status */
     {
         chan->num_packets = dwc2_calculate_packet_num(0, 0x80, USB_GET_MAXPACKETSIZE(urb->ep->wMaxPacketSize), &chan->xferlen);
@@ -438,10 +487,21 @@ static void dwc2_control_urb_init(struct usbh_bus *bus, uint8_t chidx, struct us
 static void dwc2_bulk_intr_urb_init(struct usbh_bus *bus, uint8_t chidx, struct usbh_urb *urb, uint8_t *buffer, uint32_t buflen)
 {
     struct dwc2_chan *chan;
+    uint32_t datalen;
 
     chan = &g_dwc2_hcd[bus->hcd.hcd_id].chan_pool[chidx];
 
-    chan->num_packets = dwc2_calculate_packet_num(buflen, urb->ep->bEndpointAddress, USB_GET_MAXPACKETSIZE(urb->ep->wMaxPacketSize), &chan->xferlen);
+    if (chan->do_ssplit) {
+        if (buflen > USB_GET_MAXPACKETSIZE(urb->ep->wMaxPacketSize)) {
+            datalen = USB_GET_MAXPACKETSIZE(urb->ep->wMaxPacketSize);
+        } else {
+            datalen = buflen;
+        }
+    } else {
+        datalen = buflen;
+    }
+
+    chan->num_packets = dwc2_calculate_packet_num(datalen, urb->ep->bEndpointAddress, USB_GET_MAXPACKETSIZE(urb->ep->wMaxPacketSize), &chan->xferlen);
     dwc2_chan_init(bus, chidx, urb->hport->dev_addr, urb->ep->bEndpointAddress, USB_GET_ENDPOINT_TYPE(urb->ep->bmAttributes), USB_GET_MAXPACKETSIZE(urb->ep->wMaxPacketSize), urb->hport->speed);
     dwc2_chan_transfer(bus, chidx, urb->ep->bEndpointAddress, buffer, chan->xferlen, chan->num_packets, urb->data_toggle == 0 ? HC_PID_DATA0 : HC_PID_DATA1);
 }
@@ -802,6 +862,15 @@ int usbh_submit_urb(struct usbh_urb *urb)
     chan = &g_dwc2_hcd[bus->hcd.hcd_id].chan_pool[chidx];
     chan->chidx = chidx;
     chan->urb = urb;
+    chan->do_ssplit = 0;
+
+    if (urb->hport->speed != USB_SPEED_HIGH &&
+        usbh_get_port_speed(bus, 0) == USB_SPEED_HIGH) {
+        chan->do_ssplit = 1;
+        chan->do_csplit = 0;
+        chan->hub_port = urb->hport->port;
+        chan->hub_addr = urb->hport->parent->hub_addr;
+    }
 
     urb->hcpriv = chan;
     urb->errorcode = -USB_ERR_BUSY;
@@ -919,6 +988,7 @@ static void dwc2_inchan_irq_handler(struct usbh_bus *bus, uint8_t ch_num)
             //uint32_t has_used_packets = chan->num_packets - ((USB_OTG_HC(ch_num)->HCTSIZ & USB_OTG_HCTSIZ_PKTCNT) >> 19); /* how many packets have used */
 
             urb->actual_length += count;
+            urb->transfer_buffer_length -= count;
 
             uint8_t data_toggle = ((USB_OTG_HC(ch_num)->HCTSIZ & USB_OTG_HCTSIZ_DPID) >> USB_OTG_HCTSIZ_DPID_Pos);
 
@@ -932,17 +1002,27 @@ static void dwc2_inchan_irq_handler(struct usbh_bus *bus, uint8_t ch_num)
                 usb_dcache_invalidate((uintptr_t)urb->transfer_buffer, USB_ALIGN_UP(count, CONFIG_USB_ALIGN_SIZE));
             }
 
+            chan->do_csplit = 0;
+
             if (USB_GET_ENDPOINT_TYPE(urb->ep->bmAttributes) == USB_ENDPOINT_TYPE_CONTROL) {
                 if (chan->ep0_state == DWC2_EP0_STATE_INDATA) {
-                    chan->ep0_state = DWC2_EP0_STATE_OUTSTATUS;
-                    dwc2_control_urb_init(bus, ch_num, urb, urb->setup, urb->transfer_buffer, urb->transfer_buffer_length);
+                    if (chan->do_ssplit && urb->transfer_buffer_length > 0) {
+                        dwc2_control_urb_init(bus, ch_num, urb, urb->setup, urb->transfer_buffer + urb->actual_length - 8, urb->transfer_buffer_length);
+                    } else {
+                        chan->ep0_state = DWC2_EP0_STATE_OUTSTATUS;
+                        dwc2_control_urb_init(bus, ch_num, urb, urb->setup, urb->transfer_buffer, urb->transfer_buffer_length);
+                    }
                 } else if (chan->ep0_state == DWC2_EP0_STATE_INSTATUS) {
                     chan->ep0_state = DWC2_EP0_STATE_SETUP;
                     dwc2_urb_waitup(urb);
                 }
             } else if (USB_GET_ENDPOINT_TYPE(urb->ep->bmAttributes) == USB_ENDPOINT_TYPE_ISOCHRONOUS) {
             } else {
-                dwc2_urb_waitup(urb);
+                if (chan->do_ssplit && urb->transfer_buffer_length > 0 && (count == USB_GET_MAXPACKETSIZE(urb->ep->wMaxPacketSize))) {
+                    dwc2_bulk_intr_urb_init(bus, ch_num, urb, urb->transfer_buffer + urb->actual_length, urb->transfer_buffer_length);
+                } else {
+                    dwc2_urb_waitup(urb);
+                }
             }
         } else if (chan_intstatus & USB_OTG_HCINT_AHBERR) {
             urb->errorcode = -USB_ERR_IO;
@@ -951,11 +1031,74 @@ static void dwc2_inchan_irq_handler(struct usbh_bus *bus, uint8_t ch_num)
             urb->errorcode = -USB_ERR_STALL;
             dwc2_urb_waitup(urb);
         } else if (chan_intstatus & USB_OTG_HCINT_NAK) {
-            urb->errorcode = -USB_ERR_NAK;
-            dwc2_urb_waitup(urb);
+            if (chan->do_ssplit) {
+                /*
+                 * Handle NAK for IN/OUT SSPLIT/CSPLIT transfers, bulk, control, and
+                 * interrupt. Re-start the SSPLIT transfer.
+                 */
+                switch (USB_GET_ENDPOINT_TYPE(urb->ep->bmAttributes)) {
+                    case USB_ENDPOINT_TYPE_CONTROL:
+                        chan->do_csplit = 0;
+                        dwc2_control_urb_init(bus, ch_num, urb, urb->setup, urb->transfer_buffer + urb->actual_length - 8, urb->transfer_buffer_length);
+                        break;
+                    case USB_ENDPOINT_TYPE_BULK:
+                    case USB_ENDPOINT_TYPE_INTERRUPT:
+                        chan->do_csplit = 0;
+                        dwc2_bulk_intr_urb_init(bus, ch_num, urb, urb->transfer_buffer + urb->actual_length, urb->transfer_buffer_length);
+                        break;
+                    default:
+                        break;
+                }
+            } else {
+                urb->errorcode = -USB_ERR_NAK;
+                dwc2_urb_waitup(urb);
+            }
+        } else if (chan_intstatus & USB_OTG_HCINT_ACK) {
+            if (chan->do_ssplit) {
+                /* Handle ACK on SSPLIT. ACK should not occur in CSPLIT. */
+                switch (USB_GET_ENDPOINT_TYPE(urb->ep->bmAttributes)) {
+                    case USB_ENDPOINT_TYPE_CONTROL:
+                        chan->do_csplit = 1;
+                        dwc2_control_urb_init(bus, ch_num, urb, urb->setup, urb->transfer_buffer + urb->actual_length - 8, urb->transfer_buffer_length);
+                        break;
+                    case USB_ENDPOINT_TYPE_BULK:
+                    case USB_ENDPOINT_TYPE_INTERRUPT:
+                        //printf("intr ack, len:%d\r\n", urb->actual_length);
+                        chan->do_csplit = 1;
+                        chan->ssplit_frame = dwc2_get_full_frame_num(bus);
+                        dwc2_bulk_intr_urb_init(bus, ch_num, urb, urb->transfer_buffer + urb->actual_length, urb->transfer_buffer_length);
+                        break;
+                    default:
+                        break;
+                }
+            }
         } else if (chan_intstatus & USB_OTG_HCINT_NYET) {
-            urb->errorcode = -USB_ERR_NAK;
-            dwc2_urb_waitup(urb);
+            if (chan->do_ssplit) {
+                /*
+                 * NYET on CSPLIT
+                 * re-do the CSPLIT immediately on non-periodic
+                 */
+                switch (USB_GET_ENDPOINT_TYPE(urb->ep->bmAttributes)) {
+                    case USB_ENDPOINT_TYPE_CONTROL:
+                        chan->do_csplit = 1;
+                        dwc2_control_urb_init(bus, ch_num, urb, urb->setup, urb->transfer_buffer + urb->actual_length - 8, urb->transfer_buffer_length);
+                        break;
+                    case USB_ENDPOINT_TYPE_BULK:
+                    case USB_ENDPOINT_TYPE_INTERRUPT:
+                        if ((dwc2_get_full_frame_num(bus) - chan->ssplit_frame) > 4) {
+                            chan->do_csplit = 0;
+                        } else {
+                            chan->do_csplit = 1;
+                        }
+                        dwc2_bulk_intr_urb_init(bus, ch_num, urb, urb->transfer_buffer + urb->actual_length, urb->transfer_buffer_length);
+                        break;
+                    default:
+                        break;
+                }
+            } else {
+                urb->errorcode = -USB_ERR_NAK;
+                dwc2_urb_waitup(urb);
+            }
         } else if (chan_intstatus & USB_OTG_HCINT_TXERR) {
             urb->errorcode = -USB_ERR_IO;
             dwc2_urb_waitup(urb);
@@ -992,7 +1135,16 @@ static void dwc2_outchan_irq_handler(struct usbh_bus *bus, uint8_t ch_num)
             uint32_t count = USB_OTG_HC(ch_num)->HCTSIZ & USB_OTG_HCTSIZ_XFRSIZ;                                          /* last packet size */
             uint32_t has_used_packets = chan->num_packets - ((USB_OTG_HC(ch_num)->HCTSIZ & USB_OTG_HCTSIZ_PKTCNT) >> 19); /* how many packets have used */
 
-            urb->actual_length += (has_used_packets - 1) * USB_GET_MAXPACKETSIZE(urb->ep->wMaxPacketSize) + count; //the same with urb->actual_length += chan->xferlen;
+            uint32_t olen = (has_used_packets - 1) * USB_GET_MAXPACKETSIZE(urb->ep->wMaxPacketSize) + count; //the same with urb->actual_length += chan->xferlen;
+            urb->actual_length += olen;
+
+            if (chan->ep0_state == DWC2_EP0_STATE_OUTDATA || urb->setup == NULL) {
+                if (urb->transfer_buffer_length > olen) {
+                    urb->transfer_buffer_length -= olen;
+                } else {
+                    urb->transfer_buffer_length = 0;
+                }
+            }
 
             uint8_t data_toggle = ((USB_OTG_HC(ch_num)->HCTSIZ & USB_OTG_HCTSIZ_DPID) >> USB_OTG_HCTSIZ_DPID_Pos);
 
@@ -1002,6 +1154,8 @@ static void dwc2_outchan_irq_handler(struct usbh_bus *bus, uint8_t ch_num)
                 urb->data_toggle = 1;
             }
 
+            chan->do_csplit = 0;
+
             if (USB_GET_ENDPOINT_TYPE(urb->ep->bmAttributes) == USB_ENDPOINT_TYPE_CONTROL) {
                 if (chan->ep0_state == DWC2_EP0_STATE_SETUP) {
                     if (urb->setup->wLength) {
@@ -1015,15 +1169,23 @@ static void dwc2_outchan_irq_handler(struct usbh_bus *bus, uint8_t ch_num)
                     }
                     dwc2_control_urb_init(bus, ch_num, urb, urb->setup, urb->transfer_buffer, urb->transfer_buffer_length);
                 } else if (chan->ep0_state == DWC2_EP0_STATE_OUTDATA) {
-                    chan->ep0_state = DWC2_EP0_STATE_INSTATUS;
-                    dwc2_control_urb_init(bus, ch_num, urb, urb->setup, urb->transfer_buffer, urb->transfer_buffer_length);
+                    if (chan->do_ssplit && urb->transfer_buffer_length > 0) {
+                        dwc2_control_urb_init(bus, ch_num, urb, urb->setup, urb->transfer_buffer + urb->actual_length - 8, urb->transfer_buffer_length);
+                    } else {
+                        chan->ep0_state = DWC2_EP0_STATE_INSTATUS;
+                        dwc2_control_urb_init(bus, ch_num, urb, urb->setup, urb->transfer_buffer, urb->transfer_buffer_length);
+                    }
                 } else if (chan->ep0_state == DWC2_EP0_STATE_OUTSTATUS) {
                     chan->ep0_state = DWC2_EP0_STATE_SETUP;
                     dwc2_urb_waitup(urb);
                 }
             } else if (USB_GET_ENDPOINT_TYPE(urb->ep->bmAttributes) == USB_ENDPOINT_TYPE_ISOCHRONOUS) {
             } else {
-                dwc2_urb_waitup(urb);
+                if (chan->do_ssplit && urb->transfer_buffer_length > 0) {
+                    dwc2_control_urb_init(bus, ch_num, urb, urb->setup, urb->transfer_buffer + urb->actual_length - 8, urb->transfer_buffer_length);
+                } else {
+                    dwc2_urb_waitup(urb);
+                }
             }
         } else if (chan_intstatus & USB_OTG_HCINT_AHBERR) {
             urb->errorcode = -USB_ERR_IO;
@@ -1032,11 +1194,73 @@ static void dwc2_outchan_irq_handler(struct usbh_bus *bus, uint8_t ch_num)
             urb->errorcode = -USB_ERR_STALL;
             dwc2_urb_waitup(urb);
         } else if (chan_intstatus & USB_OTG_HCINT_NAK) {
-            urb->errorcode = -USB_ERR_NAK;
-            dwc2_urb_waitup(urb);
+            if (chan->do_ssplit) {
+                /*
+                 * Handle NAK for IN/OUT SSPLIT/CSPLIT transfers, bulk, control, and
+                 * interrupt. Re-start the SSPLIT transfer.
+                 */
+                switch (USB_GET_ENDPOINT_TYPE(urb->ep->bmAttributes)) {
+                    case USB_ENDPOINT_TYPE_CONTROL:
+                        chan->do_csplit = 0;
+                        dwc2_control_urb_init(bus, ch_num, urb, urb->setup, urb->transfer_buffer + urb->actual_length - 8, urb->transfer_buffer_length);
+                        break;
+                    case USB_ENDPOINT_TYPE_BULK:
+                    case USB_ENDPOINT_TYPE_INTERRUPT:
+                        chan->do_csplit = 0;
+                        dwc2_bulk_intr_urb_init(bus, ch_num, urb, urb->transfer_buffer + urb->actual_length, urb->transfer_buffer_length);
+                        break;
+                    default:
+                        break;
+                }
+            } else {
+                urb->errorcode = -USB_ERR_NAK;
+                dwc2_urb_waitup(urb);
+            }
+        } else if (chan_intstatus & USB_OTG_HCINT_ACK) {
+            if (chan->do_ssplit) {
+                /* Handle ACK on SSPLIT. ACK should not occur in CSPLIT. */
+                switch (USB_GET_ENDPOINT_TYPE(urb->ep->bmAttributes)) {
+                    case USB_ENDPOINT_TYPE_CONTROL:
+                        chan->do_csplit = 1;
+                        dwc2_control_urb_init(bus, ch_num, urb, urb->setup, urb->transfer_buffer + urb->actual_length - 8, urb->transfer_buffer_length);
+                        break;
+                    case USB_ENDPOINT_TYPE_BULK:
+                    case USB_ENDPOINT_TYPE_INTERRUPT:
+                        chan->do_csplit = 1;
+                        chan->ssplit_frame = dwc2_get_full_frame_num(bus);
+                        dwc2_bulk_intr_urb_init(bus, ch_num, urb, urb->transfer_buffer + urb->actual_length, urb->transfer_buffer_length);
+                        break;
+                    default:
+                        break;
+                }
+            }
         } else if (chan_intstatus & USB_OTG_HCINT_NYET) {
-            urb->errorcode = -USB_ERR_NAK;
-            dwc2_urb_waitup(urb);
+            if (chan->do_ssplit) {
+                /*
+                 * NYET on CSPLIT
+                 * re-do the CSPLIT immediately on non-periodic
+                 */
+                switch (USB_GET_ENDPOINT_TYPE(urb->ep->bmAttributes)) {
+                    case USB_ENDPOINT_TYPE_CONTROL:
+                        chan->do_csplit = 1;
+                        dwc2_control_urb_init(bus, ch_num, urb, urb->setup, urb->transfer_buffer + urb->actual_length - 8, urb->transfer_buffer_length);
+                        break;
+                    case USB_ENDPOINT_TYPE_BULK:
+                    case USB_ENDPOINT_TYPE_INTERRUPT:
+                        if ((dwc2_get_full_frame_num(bus) - chan->ssplit_frame) > 4) {
+                            chan->do_csplit = 0;
+                        } else {
+                            chan->do_csplit = 1;
+                        }
+                        dwc2_bulk_intr_urb_init(bus, ch_num, urb, urb->transfer_buffer + urb->actual_length, urb->transfer_buffer_length);
+                        break;
+                    default:
+                        break;
+                }
+            } else {
+                urb->errorcode = -USB_ERR_NAK;
+                dwc2_urb_waitup(urb);
+            }
         } else if (chan_intstatus & USB_OTG_HCINT_TXERR) {
             urb->errorcode = -USB_ERR_IO;
             dwc2_urb_waitup(urb);