diff --git a/src/portable/nxp/lpc43xx_lpc18xx/dcd_lpc43xx.c b/src/portable/nxp/lpc43xx_lpc18xx/dcd_lpc43xx.c
index 07f6eef82..4ec93fb35 100644
--- a/src/portable/nxp/lpc43xx_lpc18xx/dcd_lpc43xx.c
+++ b/src/portable/nxp/lpc43xx_lpc18xx/dcd_lpc43xx.c
@@ -239,7 +239,7 @@ static inline uint8_t qtd_find_free(uint8_t rhport)
 //--------------------------------------------------------------------+
 // DCD Endpoint Port
 //--------------------------------------------------------------------+
-static inline volatile uint32_t * get_reg_control_addr(uint8_t rhport, uint8_t physical_endpoint)
+static inline volatile uint32_t * get_endpt_ctrl_reg(uint8_t rhport, uint8_t physical_endpoint)
 {
  return &(LPC_USB[rhport]->ENDPTCTRL0) + edpt_phy2log(physical_endpoint);
 }
@@ -247,7 +247,7 @@ static inline volatile uint32_t * get_reg_control_addr(uint8_t rhport, uint8_t p
 void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr)
 {
   uint8_t ep_idx    = edpt_addr2phy(ep_addr);
-  volatile uint32_t * reg_control = get_reg_control_addr(rhport, ep_idx);
+  volatile uint32_t * reg_control = get_endpt_ctrl_reg(rhport, ep_idx);
 
   if ( ep_addr == 0)
   {
@@ -267,7 +267,7 @@ bool dcd_edpt_stalled (uint8_t rhport, uint8_t ep_addr)
 
 void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr)
 {
-  volatile uint32_t * reg_control = get_reg_control_addr(rhport, edpt_addr2phy(ep_addr));
+  volatile uint32_t * reg_control = get_endpt_ctrl_reg(rhport, edpt_addr2phy(ep_addr));
 
   // data toggle also need to be reset
   (*reg_control) |= ENDPTCTRL_MASK_TOGGLE_RESET << ((ep_addr & TUSB_DIR_IN_MASK) ? 16 : 0);
@@ -280,12 +280,12 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc)
   // TODO not support ISO yet
   TU_VERIFY ( p_endpoint_desc->bmAttributes.xfer != TUSB_XFER_ISOCHRONOUS);
 
-  tusb_dir_t dir = (p_endpoint_desc->bEndpointAddress & TUSB_DIR_IN_MASK) ? TUSB_DIR_IN : TUSB_DIR_OUT;
+  uint8_t const epnum  = edpt_number(p_endpoint_desc->bEndpointAddress);
+  uint8_t const dir    = edpt_dir(p_endpoint_desc->bEndpointAddress);
+  uint8_t const ep_idx = 2*epnum + dir;
 
   //------------- Prepare Queue Head -------------//
-  uint8_t ep_idx    = edpt_addr2phy(p_endpoint_desc->bEndpointAddress);
   dcd_qhd_t * p_qhd = &dcd_data_ptr[rhport]->qhd[ep_idx];
-
   tu_memclr(p_qhd, sizeof(dcd_qhd_t));
 
   p_qhd->zero_length_termination = 1;
@@ -293,101 +293,52 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc)
   p_qhd->qtd_overlay.next        = QTD_NEXT_INVALID;
 
   //------------- Endpoint Control Register -------------//
-  volatile uint32_t * reg_control = get_reg_control_addr(rhport, ep_idx);
+  volatile uint32_t * endpt_ctrl = get_endpt_ctrl_reg(rhport, ep_idx);
 
   // endpoint must not be already enabled
-  TU_VERIFY( !( (*reg_control) &  (ENDPTCTRL_MASK_ENABLE << (dir ? 16 : 0)) ) );
+  TU_VERIFY( !( (*endpt_ctrl) &  (ENDPTCTRL_MASK_ENABLE << (dir ? 16 : 0)) ) );
 
-  (*reg_control) |= ((p_endpoint_desc->bmAttributes.xfer << 2) | ENDPTCTRL_MASK_ENABLE | ENDPTCTRL_MASK_TOGGLE_RESET) << (dir ? 16 : 0);
+  (*endpt_ctrl) |= ((p_endpoint_desc->bmAttributes.xfer << 2) | ENDPTCTRL_MASK_ENABLE | ENDPTCTRL_MASK_TOGGLE_RESET) << (dir ? 16 : 0);
 
   return true;
 }
 
 bool dcd_edpt_busy(uint8_t rhport, uint8_t ep_addr)
 {
-  uint8_t ep_idx    = edpt_addr2phy(ep_addr);
+  uint8_t const epnum  = edpt_number(ep_addr);
+  uint8_t const dir    = edpt_dir(ep_addr);
+  uint8_t const ep_idx = 2*epnum + dir;
+
   dcd_qhd_t const * p_qhd = &dcd_data_ptr[rhport]->qhd[ep_idx];
+  dcd_qtd_t * p_qtd = &dcd_data_ptr[rhport]->qtd[ep_idx];
 
-  return p_qhd->list_qtd_idx[0] != 0; // qtd list is not empty
+  return p_qtd->active;
 //  return !p_qhd->qtd_overlay.halted && p_qhd->qtd_overlay.active;
 }
 
-// control transfer does not need to use qtd find function
-// follows UM 24.10.8.1.1 Setup packet handling using setup lockout mechanism
-bool dcd_control_xfer(uint8_t rhport, uint8_t dir, uint8_t * p_buffer, uint16_t length)
-{
-  LPC_USB0_Type* const lpc_usb = LPC_USB[rhport];
-  dcd_data_t* const p_dcd      = dcd_data_ptr[rhport];
-
-  uint8_t const ep_phy = (dir == TUSB_DIR_IN) ? 1 : 0;
-
-  dcd_qhd_t* qhd = &p_dcd->qhd[ep_phy];
-
-  // wait until ENDPTSETUPSTAT before priming data/status in response TODO add time out
-  while(lpc_usb->ENDPTSETUPSTAT & BIT_(0)) {}
-
-  TU_VERIFY( !qhd->qtd_overlay.active );
-
-  dcd_qtd_t* qtd = &p_dcd->qtd[0];
-  qtd_init(qtd, p_buffer, length);
-
-  // skip xfer complete for Status
-  qtd->int_on_complete = (length > 0 ? 1 : 0);
-
-  qhd->qtd_overlay.next = (uint32_t) qtd;
-
-  lpc_usb->ENDPTPRIME = BIT_(edpt_phy2pos(ep_phy));
-
-  return true;
-}
-
-
-// add only, controller virtually cannot know
-// TODO remove and merge to dcd_edpt_xfer
-static bool pipe_add_xfer(uint8_t rhport, uint8_t ed_idx, void * buffer, uint16_t total_bytes, bool int_on_complete)
-{
-  uint8_t qtd_idx  = qtd_find_free(rhport);
-  TU_ASSERT(qtd_idx != 0);
-
-  dcd_data_t* p_dcd = dcd_data_ptr[rhport];
-  dcd_qhd_t * p_qhd = &p_dcd->qhd[ed_idx];
-  dcd_qtd_t * p_qtd = &p_dcd->qtd[qtd_idx];
-
-  //------------- Find free slot in qhd's array list -------------//
-  uint8_t free_slot;
-  for(free_slot=0; free_slot < DCD_QTD_PER_QHD_MAX; free_slot++)
-  {
-    if ( p_qhd->list_qtd_idx[free_slot] == 0 )  break; // found free slot
-  }
-  TU_ASSERT(free_slot < DCD_QTD_PER_QHD_MAX);
-
-  p_qhd->list_qtd_idx[free_slot] = qtd_idx; // add new qtd to qhd's array list
-
-  //------------- Prepare qtd -------------//
-  qtd_init(p_qtd, buffer, total_bytes);
-  p_qtd->int_on_complete = int_on_complete;
-
-  if ( free_slot > 0 ) p_dcd->qtd[ p_qhd->list_qtd_idx[free_slot-1] ].next = (uint32_t) p_qtd;
-
-  return true;
-}
-
 bool  dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes)
 {
-  if ( edpt_number(ep_addr) == 0 )
+  uint8_t const epnum = edpt_number(ep_addr);
+  uint8_t const dir   = edpt_dir(ep_addr);
+  uint8_t const ep_idx = 2*epnum + dir;
+
+  if ( epnum == 0 )
   {
-    return dcd_control_xfer(rhport, edpt_dir(ep_addr), buffer, total_bytes);
+    // follows UM 24.10.8.1.1 Setup packet handling using setup lockout mechanism
+    // wait until ENDPTSETUPSTAT before priming data/status in response TODO add time out
+    while(LPC_USB[rhport]->ENDPTSETUPSTAT & BIT_(0)) {}
   }
 
-  uint8_t ep_idx = edpt_addr2phy(ep_addr);
+  dcd_data_t* p_dcd = dcd_data_ptr[rhport];
+  dcd_qhd_t * p_qhd = &p_dcd->qhd[ep_idx];
+  dcd_qtd_t * p_qtd = &p_dcd->qtd[ep_idx];
 
-  TU_VERIFY ( pipe_add_xfer(rhport, ep_idx, buffer, total_bytes, true) );
-
-  dcd_qhd_t* p_qhd = &dcd_data_ptr[rhport]->qhd[ ep_idx ];
-  dcd_qtd_t* p_qtd = &dcd_data_ptr[rhport]->qtd[ p_qhd->list_qtd_idx[0] ];
-
-  p_qhd->qtd_overlay.next = (uint32_t) p_qtd; // attach head QTD to QHD start transferring
+  //------------- Prepare qtd -------------//
+  qtd_init(p_qtd, buffer, total_bytes);
+  p_qtd->int_on_complete = true;
+  p_qhd->qtd_overlay.next = (uint32_t) p_qtd; // link qtd to qhd
 
+  // start transfer
 	LPC_USB[rhport]->ENDPTPRIME = BIT_( edpt_phy2pos(ep_idx) ) ;
 
 	return true;
@@ -397,39 +348,6 @@ bool  dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t
 //--------------------------------------------------------------------+
 // ISR
 //--------------------------------------------------------------------+
-void xfer_complete_isr(uint8_t rhport, uint32_t reg_complete)
-{
-  for(uint8_t ep_idx = 2; ep_idx < DCD_QHD_MAX; ep_idx++)
-  {
-    if ( BIT_TEST_(reg_complete, edpt_phy2pos(ep_idx)) )
-    { // 23.10.12.3 Failed QTD also get ENDPTCOMPLETE set
-      dcd_qhd_t * p_qhd = &dcd_data_ptr[rhport]->qhd[ep_idx];
-
-      // retire all QTDs in array list, up to 1st still-active QTD
-      while( p_qhd->list_qtd_idx[0] != 0 )
-      {
-        dcd_qtd_t * p_qtd = &dcd_data_ptr[rhport]->qtd[ p_qhd->list_qtd_idx[0] ];
-
-        if (p_qtd->active)  break; // stop immediately if found still-active QTD and shift array list
-
-        //------------- Free QTD and shift array list -------------//
-        p_qtd->used = 0; // free QTD
-        memmove( (void*) p_qhd->list_qtd_idx, (void*) (p_qhd->list_qtd_idx+1), DCD_QTD_PER_QHD_MAX-1);
-        p_qhd->list_qtd_idx[DCD_QTD_PER_QHD_MAX-1]=0;
-
-        if (p_qtd->int_on_complete)
-        {
-          uint8_t result = p_qtd->halted  ? DCD_XFER_STALLED :
-                           ( p_qtd->xact_err ||p_qtd->buffer_err ) ? DCD_XFER_FAILED : DCD_XFER_SUCCESS;
-
-          uint8_t ep_addr = edpt_phy2addr(ep_idx);
-          dcd_event_xfer_complete(rhport, ep_addr, p_qtd->expected_bytes - p_qtd->total_bytes, result, true); // only number of bytes in the IOC qtd
-        }
-      }
-    }
-  }
-}
-
 void hal_dcd_isr(uint8_t rhport)
 {
   LPC_USB0_Type* const lpc_usb = LPC_USB[rhport];
@@ -478,9 +396,9 @@ void hal_dcd_isr(uint8_t rhport)
 
     dcd_data_t* const p_dcd = dcd_data_ptr[rhport];
 
-    //------------- Set up Received -------------//
     if (lpc_usb->ENDPTSETUPSTAT)
     {
+      //------------- Set up Received -------------//
       // 23.10.10.2 Operational model for setup transfers
       lpc_usb->ENDPTSETUPSTAT = lpc_usb->ENDPTSETUPSTAT;// acknowledge
 
@@ -490,29 +408,24 @@ void hal_dcd_isr(uint8_t rhport)
       dcd_event_handler(&event, true);
     }
 
-    //------------- Control Request Completed -------------//
-    else if ( edpt_complete & ( BIT_(0) | BIT_(16)) )
+    if ( edpt_complete )
     {
-      // determine Control OUT or IN
-      uint8_t ep_idx =  BIT_TEST_(edpt_complete, 0) ? 0 : 1;
-
-      // TODO use the actual QTD instead of the qhd's overlay to get expected bytes for actual byte xferred
-      dcd_qtd_t* const p_qtd =  (dcd_qtd_t*) p_dcd->qhd[ep_idx].qtd_addr;
-
-      if ( p_qtd->int_on_complete )
+      for(uint8_t ep_idx = 0; ep_idx < DCD_QHD_MAX; ep_idx++)
       {
-        uint8_t result = p_qtd->halted  ? DCD_XFER_STALLED :
-                        ( p_qtd->xact_err ||p_qtd->buffer_err ) ? DCD_XFER_FAILED : DCD_XFER_SUCCESS;
+        if ( BIT_TEST_(edpt_complete, edpt_phy2pos(ep_idx)) )
+        {
+          // 23.10.12.3 Failed QTD also get ENDPTCOMPLETE set
+          dcd_qhd_t * p_qhd = &dcd_data_ptr[rhport]->qhd[ep_idx];
+          dcd_qtd_t * p_qtd = &dcd_data_ptr[rhport]->qtd[ep_idx];
 
-        dcd_event_xfer_complete(rhport, 0, p_qtd->expected_bytes - p_qtd->total_bytes, result, true);
+          uint8_t result = p_qtd->halted  ? DCD_XFER_STALLED :
+              ( p_qtd->xact_err ||p_qtd->buffer_err ) ? DCD_XFER_FAILED : DCD_XFER_SUCCESS;
+
+          uint8_t ep_addr = edpt_phy2addr(ep_idx);
+          dcd_event_xfer_complete(rhport, ep_addr, p_qtd->expected_bytes - p_qtd->total_bytes, result, true); // only number of bytes in the IOC qtd
+        }
       }
     }
-
-    //------------- Transfer Complete -------------//
-    if ( edpt_complete & ~(BIT_(0) | BIT_(16)) )
-    {
-      xfer_complete_isr(rhport, edpt_complete);
-    }
   }
 
   if (int_status & INT_MASK_SOF)