split interrupt work with fs mouse
diff --git a/src/portable/synopsys/dwc2/hcd_dwc2.c b/src/portable/synopsys/dwc2/hcd_dwc2.c
index 3527f68..fa82691 100644
--- a/src/portable/synopsys/dwc2/hcd_dwc2.c
+++ b/src/portable/synopsys/dwc2/hcd_dwc2.c
@@ -52,6 +52,10 @@
   HCD_XFER_ERROR_MAX = 3
 };
 
+enum {
+  HCD_XFER_PERIOD_SPLIT_NYET_MAX = 3
+};
+
 //--------------------------------------------------------------------
 //
 //--------------------------------------------------------------------
@@ -67,9 +71,12 @@
     dwc2_channel_split_t hcsplt_bm;
   };
 
-  uint8_t next_pid;
-  uint16_t frame_interval;
-  uint16_t frame_countdown; // count down to make a transfer for periodic endpoint (from interval)
+  struct TU_ATTR_PACKED {
+    uint32_t uframe_interval : 18; // micro-frame interval
+    uint32_t next_pid : 2;
+  };
+
+  uint32_t uframe_countdown; // micro-frame count down to transfer for periodic, only need 18-bit
 
   uint8_t* buffer;
   uint16_t buflen;
@@ -80,7 +87,8 @@
   volatile bool allocated;
   uint8_t ep_id;
   struct TU_ATTR_PACKED {
-    uint8_t err_count : 6;
+    uint8_t err_count : 3;
+    uint8_t period_split_nyet_count : 3;
     uint8_t do_ping : 1;
     uint8_t sof_schedule : 1;
   };
@@ -101,9 +109,9 @@
 //--------------------------------------------------------------------
 //
 //--------------------------------------------------------------------
-TU_ATTR_ALWAYS_INLINE static inline tusb_speed_t convert_hprt_speed(uint32_t hprt_speed) {
+TU_ATTR_ALWAYS_INLINE static inline tusb_speed_t hprt_speed_get(dwc2_regs_t* dwc2) {
   tusb_speed_t speed;
-  switch(hprt_speed) {
+  switch(dwc2->hprt_bm.speed) {
     case HPRT_SPEED_HIGH: speed = TUSB_SPEED_HIGH; break;
     case HPRT_SPEED_FULL: speed = TUSB_SPEED_FULL; break;
     case HPRT_SPEED_LOW : speed = TUSB_SPEED_LOW ; break;
@@ -411,7 +419,7 @@
 // Get port link speed
 tusb_speed_t hcd_port_speed_get(uint8_t rhport) {
   dwc2_regs_t* dwc2 = DWC2_REG(rhport);
-  const tusb_speed_t speed = convert_hprt_speed(dwc2->hprt_bm.speed);
+  const tusb_speed_t speed = hprt_speed_get(dwc2);
   return speed;
 }
 
@@ -433,7 +441,7 @@
 // Open an endpoint
 bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, const tusb_desc_endpoint_t* desc_ep) {
   dwc2_regs_t* dwc2 = DWC2_REG(rhport);
-  const tusb_speed_t rh_speed = convert_hprt_speed(dwc2->hprt_bm.speed);
+  const tusb_speed_t rh_speed = hprt_speed_get(dwc2);
 
   hcd_devtree_info_t devtree_info;
   hcd_devtree_get_info(dev_addr, &devtree_info);
@@ -464,12 +472,15 @@
 
   edpt->next_pid = HCTSIZ_PID_DATA0;
   if (desc_ep->bmAttributes.xfer == TUSB_XFER_ISOCHRONOUS) {
-    edpt->frame_interval = 1 << (desc_ep->bInterval - 1);
+    edpt->uframe_interval = 1 << (desc_ep->bInterval - 1);
+    if (devtree_info.speed == TUSB_SPEED_FULL) {
+      edpt->uframe_interval <<= 3;
+    }
   } else if (desc_ep->bmAttributes.xfer == TUSB_XFER_INTERRUPT) {
     if (devtree_info.speed == TUSB_SPEED_HIGH) {
-      edpt->frame_interval = 1 << (desc_ep->bInterval - 1);
+      edpt->uframe_interval = 1 << (desc_ep->bInterval - 1);
     } else {
-      edpt->frame_interval  = desc_ep->bInterval;
+      edpt->uframe_interval = desc_ep->bInterval << 3;
     }
   }
 
@@ -669,9 +680,22 @@
   hcd_endpoint_t* edpt = &_hcd_data.edpt[xfer->ep_id];
 
   if (edpt_is_periodic(channel->hcchar_bm.ep_type)){
+    // retry immediately for periodic split nyet if haven't reach max retry
+    if (channel->hcsplt_bm.split_en && channel->hcsplt_bm.split_compl && (hcint & HCINT_NYET)) {
+      xfer->period_split_nyet_count++;
+      if (xfer->period_split_nyet_count < HCD_XFER_PERIOD_SPLIT_NYET_MAX) {
+        channel->hcchar_bm.odd_frame = 1 - (dwc2->hfnum & 1); // transfer on next frame
+        channel_send_in_token(dwc2, channel);
+        return;
+      } else {
+        // too many NYET, de-allocate channel with below code
+        xfer->period_split_nyet_count = 0;
+      }
+    }
+
     // for periodic, de-allocate channel, enable SOF set frame counter for later transfer
     edpt->next_pid = channel->hctsiz_bm.pid; // save PID
-    edpt->frame_countdown = edpt->frame_interval;
+    edpt->uframe_countdown = edpt->uframe_interval;
     dwc2->gintmsk |= GINTSTS_SOF;
 
     if (hcint & HCINT_HALTED) {
@@ -683,10 +707,7 @@
       channel_disable(dwc2, channel);
     }
   } else {
-    // for control/bulk: try again immediately
-    if (channel->hcsplt_bm.split_en) {
-      channel->hcsplt_bm.split_compl = 0; // retry with start split
-    }
+    // for control/bulk: retry immediately
     channel_send_in_token(dwc2, channel);
   }
 }
@@ -901,6 +922,7 @@
         edpt->buffer += actual_len;
         edpt->buflen -= actual_len;
 
+        channel->hcsplt_bm.split_compl = 0;
         channel_xfer_in_retry(dwc2, ch_id, hcint);
       } else {
         xfer->result = XFER_RESULT_SUCCESS;
@@ -915,29 +937,37 @@
         xfer->result = XFER_RESULT_FAILED;
       } else {
         channel->hcintmsk |= HCINT_ACK | HCINT_NAK | HCINT_DATATOGGLE_ERR;
+        channel->hcsplt_bm.split_compl = 0;
         channel_xfer_in_retry(dwc2, ch_id, hcint);
       }
-    } else if (hcint & (HCINT_NAK | HCINT_DATATOGGLE_ERR)) {
-      xfer->err_count = 0;
-      channel->hcintmsk &= ~(HCINT_NAK | HCINT_DATATOGGLE_ERR);
-      if ((hcint & (HCINT_NAK | HCINT_DATATOGGLE_ERR))) {
+    } else if (hcint & HCINT_NYET) {
+      // Must handle nyet before nak or ack. Could get a nyet at the same time as either of those on a BULK/CONTROL
+      // OUT that started with a PING. The nyet takes precedence.
+      if (channel->hcsplt_bm.split_en) {
+        // split not yet mean hub has no data, retry complete split
+        channel->hcsplt_bm.split_compl = 1;
         channel_xfer_in_retry(dwc2, ch_id, hcint);
       }
     } else if (hcint & HCINT_ACK) {
       xfer->err_count = 0;
       channel->hcintmsk &= ~HCINT_ACK;
-
-      if (channel->hcsplt_bm.split_en && !channel->hcsplt_bm.split_compl) {
+      if (channel->hcsplt_bm.split_en) {
         // start split is ACK --> do complete split
+        // TODO: for ISO must use xact_pos to plan complete split based on microframe (up to 187.5 bytes/uframe)
         channel->hcsplt_bm.split_compl = 1;
+        if (edpt_is_periodic(channel->hcchar_bm.ep_type)) {
+          channel->hcchar_bm.odd_frame = 1 - (dwc2->hfnum & 1); // transfer on next frame
+        }
         channel_send_in_token(dwc2, channel);
       }
-    } else if (hcint & HCINT_NYET) {
-      if (channel->hcsplt_bm.split_en && channel->hcsplt_bm.split_compl) {
-        // split not yet mean hub has no data, retry complete split
-        channel->hcsplt_bm.split_compl = 1;
-        channel_send_in_token(dwc2, channel);
-      }
+    } else if (hcint & (HCINT_NAK | HCINT_DATATOGGLE_ERR)) {
+      xfer->err_count = 0;
+      channel->hcintmsk &= ~(HCINT_NAK | HCINT_DATATOGGLE_ERR);
+      channel->hcsplt_bm.split_compl = 0; // restart with start-split
+      channel_xfer_in_retry(dwc2, ch_id, hcint);
+    } else if (hcint & HCINT_FARME_OVERRUN) {
+      // retry start-split in next binterval
+      channel_xfer_in_retry(dwc2, ch_id, hcint);
     }
   }
 
@@ -982,6 +1012,12 @@
          channel_xfer_start(dwc2, ch_id);
        }
      }
+    } else if (hcint & HCINT_NYET) {
+      if (channel->hcsplt_bm.split_en && channel->hcsplt_bm.split_compl) {
+        // split not yet mean hub has no data, retry complete split
+        channel->hcsplt_bm.split_compl = 1;
+        channel->hcchar |= HCCHAR_CHENA;
+      }
     } else if (hcint & HCINT_ACK) {
       xfer->err_count = 0;
       if (channel->hcsplt_bm.split_en && !channel->hcsplt_bm.split_compl) {
@@ -989,12 +1025,6 @@
         channel->hcsplt_bm.split_compl = 1;
         channel->hcchar |= HCCHAR_CHENA;
       }
-    } else if (hcint & HCINT_NYET) {
-      if (channel->hcsplt_bm.split_en && channel->hcsplt_bm.split_compl) {
-        // split not yet mean hub has no data, retry complete split
-        channel->hcsplt_bm.split_compl = 1;
-        channel->hcchar |= HCCHAR_CHENA;
-      }
     }
   } else if (hcint & HCINT_ACK) {
     xfer->err_count = 0;
@@ -1055,13 +1085,16 @@
 
   bool more_isr = false;
 
+  // If highspeed then SOF is 125us, else 1ms
+  const uint32_t ucount = (hprt_speed_get(dwc2) == TUSB_SPEED_HIGH ? 1 : 8);
+
   for(uint8_t ep_id = 0; ep_id < CFG_TUH_DWC2_ENDPOINT_MAX; ep_id++) {
     hcd_endpoint_t* edpt = &_hcd_data.edpt[ep_id];
-    if (edpt->hcchar_bm.enable && edpt_is_periodic(edpt->hcchar_bm.ep_type) && edpt->frame_countdown > 0) {
-      edpt->frame_countdown--;
-      if (edpt->frame_countdown == 0) {
+    if (edpt->hcchar_bm.enable && edpt_is_periodic(edpt->hcchar_bm.ep_type) && edpt->uframe_countdown > 0) {
+      edpt->uframe_countdown -= tu_min32(ucount, edpt->uframe_countdown);
+      if (edpt->uframe_countdown == 0) {
         if (!edpt_xfer_kickoff(dwc2, ep_id)) {
-          edpt->frame_countdown = 1; // failed to start (out of channel), try again next frame
+          edpt->uframe_countdown = ucount; // failed to start, try again next frame
         }
       }
 
@@ -1073,7 +1106,8 @@
 }
 
 // Config HCFG FS/LS clock and HFIR for SOF interval according to link speed (value is in PHY clock unit)
-static void port0_enable(dwc2_regs_t* dwc2, tusb_speed_t speed) {
+static void port0_enable(dwc2_regs_t* dwc2) {
+  const tusb_speed_t speed = hprt_speed_get(dwc2);
   uint32_t hcfg = dwc2->hcfg & ~HCFG_FSLS_PHYCLK_SEL;
 
   const dwc2_gusbcfg_t gusbcfg_bm = dwc2->gusbcfg_bm;
@@ -1143,8 +1177,7 @@
 
     if (hprt_bm.enable) {
       // Port enable
-      const tusb_speed_t speed = convert_hprt_speed(hprt_bm.speed);
-      port0_enable(dwc2, speed);
+      port0_enable(dwc2);
     } else {
       // TU_ASSERT(false, );
     }