diff --git a/src/class/cdc/cdc_device.c b/src/class/cdc/cdc_device.c
index a6f1f1fbb..f3d4622f8 100644
--- a/src/class/cdc/cdc_device.c
+++ b/src/class/cdc/cdc_device.c
@@ -289,8 +289,6 @@ tusb_error_t cdcd_open(uint8_t rhport, tusb_desc_interface_t const * p_interface
 
 tusb_error_t cdcd_control_request_st(uint8_t rhport, tusb_control_request_t const * p_request)
 {
-  OSAL_SUBTASK_BEGIN
-
   //------------- Class Specific Request -------------//
   if (p_request->bmRequestType_bit.type != TUSB_REQ_TYPE_CLASS) return TUSB_ERROR_DCD_CONTROL_REQUEST_NOT_SUPPORT;
 
@@ -301,7 +299,7 @@ tusb_error_t cdcd_control_request_st(uint8_t rhport, tusb_control_request_t cons
   if ( (CDC_REQUEST_GET_LINE_CODING == p_request->bRequest) || (CDC_REQUEST_SET_LINE_CODING == p_request->bRequest) )
   {
     uint16_t len = tu_min16(sizeof(cdc_line_coding_t), p_request->wLength);
-    usbd_control_xfer_st(rhport, p_request->bmRequestType_bit.direction, &p_cdc->line_coding, len);
+    usbd_control_xfer_st(rhport, p_request->bmRequestType_bit.direction, (uint8_t*) &p_cdc->line_coding, len);
 
     // Invoke callback
     if (CDC_REQUEST_SET_LINE_CODING == p_request->bRequest)
@@ -327,8 +325,7 @@ tusb_error_t cdcd_control_request_st(uint8_t rhport, tusb_control_request_t cons
   {
     dcd_control_stall(rhport); // stall unsupported request
   }
-
-  OSAL_SUBTASK_END
+  return TUSB_ERROR_NONE;
 }
 
 tusb_error_t cdcd_xfer_cb(uint8_t rhport, uint8_t ep_addr, tusb_event_t event, uint32_t xferred_bytes)
diff --git a/src/class/hid/hid_device.c b/src/class/hid/hid_device.c
index d8696f137..3e74950e6 100644
--- a/src/class/hid/hid_device.c
+++ b/src/class/hid/hid_device.c
@@ -407,8 +407,6 @@ tusb_error_t hidd_control_request_st(uint8_t rhport, tusb_control_request_t cons
   hidd_interface_t* p_hid = get_interface_by_itfnum( (uint8_t) p_request->wIndex );
   TU_ASSERT(p_hid, TUSB_ERROR_FAILED);
 
-  OSAL_SUBTASK_BEGIN
-
   //------------- STD Request -------------//
   if (p_request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD)
   {
@@ -419,7 +417,7 @@ tusb_error_t hidd_control_request_st(uint8_t rhport, tusb_control_request_t cons
     if (p_request->bRequest == TUSB_REQ_GET_DESCRIPTOR && desc_type == HID_DESC_TYPE_REPORT)
     {
       // use device control buffer
-      STASK_ASSERT ( p_hid->desc_len <= CFG_TUD_CTRL_BUFSIZE );
+      TU_ASSERT ( p_hid->desc_len <= CFG_TUD_CTRL_BUFSIZE );
       memcpy(_usbd_ctrl_buf, p_hid->desc_report, p_hid->desc_len);
 
       usbd_control_xfer_st(rhport, p_request->bmRequestType_bit.direction, _usbd_ctrl_buf, p_hid->desc_len);
@@ -447,7 +445,7 @@ tusb_error_t hidd_control_request_st(uint8_t rhport, tusb_control_request_t cons
         xferlen = p_request->wLength;
       }
 
-      STASK_ASSERT( xferlen > 0 );
+      TU_ASSERT( xferlen > 0 );
       usbd_control_xfer_st(rhport, p_request->bmRequestType_bit.direction, p_hid->report_buf, xferlen);
     }
     else if ( HID_REQ_CONTROL_SET_REPORT == p_request->bRequest )
@@ -492,8 +490,7 @@ tusb_error_t hidd_control_request_st(uint8_t rhport, tusb_control_request_t cons
   {
     dcd_control_stall(rhport);
   }
-
-  OSAL_SUBTASK_END
+  return TUSB_ERROR_NONE;
 }
 
 tusb_error_t hidd_xfer_cb(uint8_t rhport, uint8_t edpt_addr, tusb_event_t event, uint32_t xferred_bytes)
diff --git a/src/class/msc/msc.h b/src/class/msc/msc.h
index 59df6bed6..a0abc2501 100644
--- a/src/class/msc/msc.h
+++ b/src/class/msc/msc.h
@@ -279,11 +279,13 @@ typedef struct ATTR_PACKED
 
 TU_VERIFY_STATIC( sizeof(scsi_mode_sense6_t) == 6, "size is not correct");
 
+// This is only a Mode parameter header(6).
 typedef struct ATTR_PACKED
 {
   uint8_t data_len;
   uint8_t medium_type;
-  uint8_t device_specific_para;
+  bool write_protected : 1;
+  uint8_t reserved : 7;
   uint8_t block_descriptor_len;
 } scsi_mode_sense6_resp_t;
 
diff --git a/src/class/msc/msc_device.c b/src/class/msc/msc_device.c
index 5a047b016..aa9ebbbc9 100644
--- a/src/class/msc/msc_device.c
+++ b/src/class/msc/msc_device.c
@@ -59,31 +59,7 @@ enum
   MSC_STAGE_STATUS
 };
 
-typedef struct {
-  CFG_TUSB_MEM_ALIGN msc_cbw_t  cbw;
-
-//#if defined (__ICCARM__) && (CFG_TUSB_MCU == OPT_MCU_LPC11UXX || CFG_TUSB_MCU == OPT_MCU_LPC13UXX)
-//  uint8_t padding1[64-sizeof(msc_cbw_t)]; // IAR cannot align struct's member
-//#endif
-
-  CFG_TUSB_MEM_ALIGN msc_csw_t csw;
-
-  uint8_t  itf_num;
-  uint8_t  ep_in;
-  uint8_t  ep_out;
-
-  // Bulk Only Transfer (BOT) Protocol
-  uint8_t  stage;
-  uint32_t total_len;
-  uint32_t xferred_len; // numbered of bytes transferred so far in the Data Stage
-
-  // Sense Response Data
-  uint8_t sense_key;
-  uint8_t add_sense_code;
-  uint8_t add_sense_qualifier;
-}mscd_interface_t;
-
-CFG_TUSB_ATTR_USBRAM CFG_TUSB_MEM_ALIGN static mscd_interface_t _mscd_itf;
+CFG_TUSB_ATTR_USBRAM CFG_TUSB_MEM_ALIGN mscd_interface_t _mscd_itf;
 CFG_TUSB_ATTR_USBRAM CFG_TUSB_MEM_ALIGN static uint8_t _mscd_buf[CFG_TUD_MSC_BUFSIZE];
 
 //--------------------------------------------------------------------+
@@ -172,8 +148,6 @@ tusb_error_t mscd_open(uint8_t rhport, tusb_desc_interface_t const * p_desc_itf,
 
 tusb_error_t mscd_control_request_st(uint8_t rhport, tusb_control_request_t const * p_request)
 {
-  OSAL_SUBTASK_BEGIN
-
   TU_ASSERT(p_request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS, TUSB_ERROR_DCD_CONTROL_REQUEST_NOT_SUPPORT);
 
   if(MSC_REQ_RESET == p_request->bRequest)
@@ -189,10 +163,19 @@ tusb_error_t mscd_control_request_st(uint8_t rhport, tusb_control_request_t cons
   {
     dcd_control_stall(rhport); // stall unsupported request
   }
-
-  OSAL_SUBTASK_END
+  return TUSB_ERROR_NONE;
 }
 
+// For backwards compatibility we support static block counts.
+#if defined(CFG_TUD_MSC_BLOCK_NUM) && defined(CFG_TUD_MSC_BLOCK_SZ)
+ATTR_WEAK bool tud_lun_capacity_cb(uint8_t lun, uint32_t* last_valid_sector, uint16_t* block_size) {
+    (void) lun;
+    *last_valid_sector = CFG_TUD_MSC_BLOCK_NUM-1;
+    *block_size = CFG_TUD_MSC_BLOCK_SZ;
+    return true;
+}
+#endif
+
 // return length of response (copied to buffer), -1 if it is not an built-in commands
 int32_t proc_builtin_scsi(msc_cbw_t const * p_cbw, uint8_t* buffer, uint32_t bufsize)
 {
@@ -202,11 +185,13 @@ int32_t proc_builtin_scsi(msc_cbw_t const * p_cbw, uint8_t* buffer, uint32_t buf
   {
     case SCSI_CMD_READ_CAPACITY_10:
     {
-      scsi_read_capacity10_resp_t read_capa10 =
-      {
-          .last_lba   = ENDIAN_BE(CFG_TUD_MSC_BLOCK_NUM-1), // read capacity
-          .block_size = ENDIAN_BE(CFG_TUD_MSC_BLOCK_SZ)
-      };
+      scsi_read_capacity10_resp_t read_capa10;
+
+      uint32_t last_valid_sector;
+      uint16_t block_size;
+      tud_lun_capacity_cb(p_cbw->lun, &last_valid_sector, &block_size);
+      read_capa10.last_lba = ENDIAN_BE(last_valid_sector); // read capacity
+      read_capa10.block_size = ENDIAN_BE(block_size);
 
       ret = sizeof(read_capa10);
       memcpy(buffer, &read_capa10, ret);
@@ -218,11 +203,17 @@ int32_t proc_builtin_scsi(msc_cbw_t const * p_cbw, uint8_t* buffer, uint32_t buf
       scsi_read_format_capacity_data_t read_fmt_capa =
       {
           .list_length     = 8,
-          .block_num       = ENDIAN_BE(CFG_TUD_MSC_BLOCK_NUM),  // write capacity
+          .block_num       = 0,
           .descriptor_type = 2,                                 // formatted media
-          .block_size_u16  = ENDIAN_BE16(CFG_TUD_MSC_BLOCK_SZ)
+          .block_size_u16  = 0
       };
 
+      uint32_t last_valid_sector;
+      uint16_t block_size;
+      tud_lun_capacity_cb(p_cbw->lun, &last_valid_sector, &block_size);
+      read_fmt_capa.block_num = ENDIAN_BE(last_valid_sector+1);
+      read_fmt_capa.block_size_u16 = ENDIAN_BE16(block_size);
+
       ret = sizeof(read_fmt_capa);
       memcpy(buffer, &read_fmt_capa, ret);
     }
@@ -251,13 +242,21 @@ int32_t proc_builtin_scsi(msc_cbw_t const * p_cbw, uint8_t* buffer, uint32_t buf
 
     case SCSI_CMD_MODE_SENSE_6:
     {
-      scsi_mode_sense6_resp_t const mode_resp = {
-        .data_len = 3,
-        .medium_type = 0,
-        .device_specific_para = 0,
-        .block_descriptor_len = 0    // no block descriptor are included
+      scsi_mode_sense6_resp_t mode_resp =
+      {
+          .data_len = 3,
+          .medium_type = 0,
+          .write_protected = false,
+          .reserved = 0,
+          .block_descriptor_len = 0  // no block descriptor are included
       };
 
+      bool writable = true;
+      if (tud_msc_is_writable_cb) {
+          writable = tud_msc_is_writable_cb(p_cbw->lun);
+      }
+      mode_resp.write_protected = !writable;
+
       ret = sizeof(mode_resp);
       memcpy(buffer, &mode_resp, ret);
     }
@@ -291,7 +290,7 @@ int32_t proc_builtin_scsi(msc_cbw_t const * p_cbw, uint8_t* buffer, uint32_t buf
   return ret;
 }
 
-tusb_error_t mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, tusb_event_t event, uint32_t xferred_bytes)
+tusb_error_t mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, uint8_t event, uint32_t xferred_bytes)
 {
   mscd_interface_t* p_msc = &_mscd_itf;
   msc_cbw_t const * p_cbw = &p_msc->cbw;
@@ -425,7 +424,7 @@ tusb_error_t mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, tusb_event_t event, u
           }else
           {
             // Application consume less than what we got (including zero)
-            if ( nbytes < xferred_bytes )
+            if ( nbytes < (int32_t) xferred_bytes )
             {
               if ( nbytes > 0 )
               {
diff --git a/src/class/msc/msc_device.h b/src/class/msc/msc_device.h
index 8403dfed9..92ab4dde0 100644
--- a/src/class/msc/msc_device.h
+++ b/src/class/msc/msc_device.h
@@ -55,14 +55,6 @@ TU_VERIFY_STATIC(CFG_TUD_MSC_BUFSIZE < UINT16_MAX, "Size is not correct");
   #error MSC Device: Incorrect setting of MAX LUN
 #endif
 
-#ifndef CFG_TUD_MSC_BLOCK_NUM
-  #error CFG_TUD_MSC_BLOCK_NUM must be defined
-#endif
-
-#ifndef CFG_TUD_MSC_BLOCK_SZ
-  #error CFG_TUD_MSC_BLOCK_SZ must be defined
-#endif
-
 #ifndef CFG_TUD_MSC_BUFSIZE
   #error CFG_TUD_MSC_BUFSIZE must be defined, value of CFG_TUD_MSC_BLOCK_SZ should work well, the more the better
 #endif
@@ -89,6 +81,32 @@ TU_VERIFY_STATIC(CFG_TUD_MSC_BUFSIZE < UINT16_MAX, "Size is not correct");
  extern "C" {
 #endif
 
+typedef struct {
+  CFG_TUSB_MEM_ALIGN msc_cbw_t  cbw;
+
+//#if defined (__ICCARM__) && (CFG_TUSB_MCU == OPT_MCU_LPC11UXX || CFG_TUSB_MCU == OPT_MCU_LPC13UXX)
+//  uint8_t padding1[64-sizeof(msc_cbw_t)]; // IAR cannot align struct's member
+//#endif
+
+  CFG_TUSB_MEM_ALIGN msc_csw_t csw;
+
+  uint8_t  itf_num;
+  uint8_t  ep_in;
+  uint8_t  ep_out;
+
+  // Bulk Only Transfer (BOT) Protocol
+  uint8_t  stage;
+  uint32_t total_len;
+  uint32_t xferred_len; // numbered of bytes transferred so far in the Data Stage
+
+  // Sense Response Data
+  uint8_t sense_key;
+  uint8_t add_sense_code;
+  uint8_t add_sense_qualifier;
+}mscd_interface_t;
+
+extern mscd_interface_t _mscd_itf;
+
 /** \addtogroup ClassDriver_MSC
  *  @{
  * \defgroup MSC_Device Device
@@ -138,7 +156,7 @@ int32_t tud_msc_read10_cb (uint8_t lun, uint32_t lba, uint32_t offset, void* buf
  * \retval      negative    Indicate error writing disk I/O. Tinyusb will \b STALL the corresponding
  *                          endpoint and return failed status in command status wrapper phase.
  */
-int32_t tud_msc_write10_cb (uint8_t lun, uint32_t lba, uint32_t offset, void* buffer, uint32_t bufsize);
+int32_t tud_msc_write10_cb (uint8_t lun, uint32_t lba, uint32_t offset, uint8_t* buffer, uint32_t bufsize);
 
 /**
  * Callback invoked when received an SCSI command not in built-in list below.
@@ -164,6 +182,12 @@ ATTR_WEAK void tud_msc_read10_complete_cb(uint8_t lun);
 ATTR_WEAK void tud_msc_write10_complete_cb(uint8_t lun);
 ATTR_WEAK void tud_msc_scsi_complete_cb(uint8_t lun, uint8_t const scsi_cmd[16]);
 
+// Hook to make a mass storage device read-only.
+ATTR_WEAK bool tud_msc_is_writable_cb(uint8_t lun);
+
+// Override for dynamic LUN sizes.
+ATTR_WEAK bool tud_lun_capacity_cb(uint8_t lun, uint32_t* last_valid_sector, uint16_t* block_size);
+
 /** @} */
 /** @} */
 
@@ -185,4 +209,3 @@ void mscd_reset(uint8_t rhport);
 #endif
 
 #endif /* _TUSB_MSC_DEVICE_H_ */
-
diff --git a/src/device/dcd.h b/src/device/dcd.h
index 87837df20..c1715a5b7 100644
--- a/src/device/dcd.h
+++ b/src/device/dcd.h
@@ -113,32 +113,13 @@ void dcd_disconnect       (uint8_t rhport) ATTR_WEAK;
 void dcd_event_handler(dcd_event_t const * event, bool in_isr);
 
 // helper to send bus signal event
-static inline void dcd_event_bus_signal (uint8_t rhport, dcd_eventid_t eid, bool in_isr)
-{
-  dcd_event_t event = { .rhport = 0, .event_id = eid, };
-  dcd_event_handler(&event, in_isr);
-}
+void dcd_event_bus_signal (uint8_t rhport, dcd_eventid_t eid, bool in_isr);
 
 // helper to send setup received
-static inline void dcd_event_setup_recieved(uint8_t rhport, uint8_t const * setup, bool in_isr)
-{
-  dcd_event_t event = { .rhport = 0, .event_id = DCD_EVENT_SETUP_RECEIVED };
-  memcpy(&event.setup_received, setup, 8);
-
-  dcd_event_handler(&event, true);
-}
+void dcd_event_setup_received(uint8_t rhport, uint8_t const * setup, bool in_isr);
 
 // helper to send transfer complete event
-static inline void dcd_event_xfer_complete (uint8_t rhport, uint8_t ep_addr, uint32_t xferred_bytes, uint8_t result, bool in_isr)
-{
-  dcd_event_t event = { .rhport = 0, .event_id = DCD_EVENT_XFER_COMPLETE };
-
-  event.xfer_complete.ep_addr = ep_addr;
-  event.xfer_complete.len     = xferred_bytes;
-  event.xfer_complete.result  = result;
-
-  dcd_event_handler(&event, in_isr);
-}
+void dcd_event_xfer_complete (uint8_t rhport, uint8_t ep_addr, uint32_t xferred_bytes, uint8_t result, bool in_isr);
 
 
 /*------------------------------------------------------------------*/
@@ -167,7 +148,7 @@ static inline bool dcd_control_status(uint8_t rhport, uint8_t dir)
 
 static inline void dcd_control_stall(uint8_t rhport)
 {
-  dcd_edpt_stall(rhport, 0);
+  dcd_edpt_stall(rhport, 0 | TUSB_DIR_IN_MASK);
 }
 
 #ifdef __cplusplus
diff --git a/src/device/usbd.c b/src/device/usbd.c
index 1c2fa5d5d..8155b585f 100644
--- a/src/device/usbd.c
+++ b/src/device/usbd.c
@@ -239,25 +239,25 @@ void usbd_task( void* param)
   OSAL_TASK_END
 }
 
+extern uint32_t setup_count;
+
 static tusb_error_t usbd_main_st(void)
 {
-  static dcd_event_t event;
-
-  OSAL_SUBTASK_BEGIN
-
+  dcd_event_t event;
+  tusb_error_t err = TUSB_ERROR_NONE;
   // Loop until there is no more events in the queue
-  while (1)
+  while (_usbd_q->count > 0)
   {
-    uint32_t err;
-
-    err = TUSB_ERROR_NONE;
     tu_memclr(&event, sizeof(dcd_event_t));
 
-    osal_queue_receive(_usbd_q, &event, OSAL_TIMEOUT_WAIT_FOREVER, &err);
+    err = osal_queue_receive(_usbd_q, &event);
+    if (err != TUSB_ERROR_NONE) {
+        break;
+    }
 
     if ( DCD_EVENT_SETUP_RECEIVED == event.event_id )
     {
-      STASK_INVOKE( proc_control_request_st(event.rhport, &event.setup_received), err );
+      proc_control_request_st(event.rhport, &event.setup_received);
     }
     else if (DCD_EVENT_XFER_COMPLETE == event.event_id)
     {
@@ -267,7 +267,7 @@ static tusb_error_t usbd_main_st(void)
 
       if (drv_id < USBD_CLASS_DRIVER_COUNT)
       {
-        usbd_class_drivers[drv_id].xfer_cb( event.rhport, ep_addr, (tusb_event_t) event.xfer_complete.result, event.xfer_complete.len);
+        usbd_class_drivers[drv_id].xfer_cb( event.rhport, ep_addr, event.xfer_complete.result, event.xfer_complete.len);
       }
     }
     else if (DCD_EVENT_BUS_RESET == event.event_id)
@@ -304,7 +304,7 @@ static tusb_error_t usbd_main_st(void)
     }
   }
 
-  OSAL_SUBTASK_END
+    return err;
 }
 
 //--------------------------------------------------------------------+
@@ -312,10 +312,7 @@ static tusb_error_t usbd_main_st(void)
 //--------------------------------------------------------------------+
 static tusb_error_t proc_control_request_st(uint8_t rhport, tusb_control_request_t const * const p_request)
 {
-  OSAL_SUBTASK_BEGIN
-
-  ATTR_UNUSED tusb_error_t error;
-  error = TUSB_ERROR_NONE;
+  tusb_error_t error = TUSB_ERROR_NONE;
 
   //------------- Standard Request e.g in enumeration -------------//
   if( TUSB_REQ_RCPT_DEVICE    == p_request->bmRequestType_bit.recipient &&
@@ -326,9 +323,10 @@ static tusb_error_t proc_control_request_st(uint8_t rhport, tusb_control_request
       uint8_t  const * buffer = NULL;
       uint16_t const   len    = get_descriptor(rhport, p_request, &buffer);
 
+
       if ( len )
       {
-        STASK_ASSERT( len <= CFG_TUD_CTRL_BUFSIZE );
+        TU_ASSERT( len <= CFG_TUD_CTRL_BUFSIZE );
         memcpy(_usbd_ctrl_buf, buffer, len);
         usbd_control_xfer_st(rhport, p_request->bmRequestType_bit.direction, _usbd_ctrl_buf, len);
       }else
@@ -365,7 +363,7 @@ static tusb_error_t proc_control_request_st(uint8_t rhport, tusb_control_request
   {
     if (_usbd_dev.itf2drv[ tu_u16_low(p_request->wIndex) ] < USBD_CLASS_DRIVER_COUNT)
     {
-      STASK_INVOKE( usbd_class_drivers[ _usbd_dev.itf2drv[ tu_u16_low(p_request->wIndex) ] ].control_req_st(rhport, p_request), error );
+      error = usbd_class_drivers[ _usbd_dev.itf2drv[ tu_u16_low(p_request->wIndex) ] ].control_req_st(rhport, p_request);
     }else
     {
       dcd_control_stall(rhport); // Stall unsupported request
@@ -406,8 +404,10 @@ static tusb_error_t proc_control_request_st(uint8_t rhport, tusb_control_request
   {
     dcd_control_stall(rhport); // Stall unsupported request
   }
-
-  OSAL_SUBTASK_END
+  if (error != TUSB_ERROR_NONE) {
+    dcd_control_stall(rhport); // Stall errored requests
+  }
+  return error;
 }
 
 // Process Set Configure Request
@@ -548,8 +548,6 @@ static void mark_interface_endpoint(uint8_t const* p_desc, uint16_t desc_len, ui
 //--------------------------------------------------------------------+
 void dcd_event_handler(dcd_event_t const * event, bool in_isr)
 {
-  uint8_t const rhport = event->rhport;
-
   switch (event->event_id)
   {
     case DCD_EVENT_BUS_RESET:
@@ -590,9 +588,49 @@ void dcd_event_handler(dcd_event_t const * event, bool in_isr)
   }
 }
 
+void dcd_event_handler(dcd_event_t const * event, bool in_isr);
+
+// helper to send bus signal event
+void dcd_event_bus_signal (uint8_t rhport, dcd_eventid_t eid, bool in_isr)
+{
+  dcd_event_t event = { .rhport = 0, .event_id = eid, };
+  dcd_event_handler(&event, in_isr);
+}
+
+// helper to send setup received
+void dcd_event_setup_received(uint8_t rhport, uint8_t const * setup, bool in_isr)
+{
+  dcd_event_t event = { .rhport = 0, .event_id = DCD_EVENT_SETUP_RECEIVED };
+  memcpy(&event.setup_received, setup, 8);
+
+  dcd_event_handler(&event, true);
+}
+
+// helper to send transfer complete event
+void dcd_event_xfer_complete (uint8_t rhport, uint8_t ep_addr, uint32_t xferred_bytes, uint8_t result, bool in_isr)
+{
+  dcd_event_t event = { .rhport = 0, .event_id = DCD_EVENT_XFER_COMPLETE };
+
+  event.xfer_complete.ep_addr = ep_addr;
+  event.xfer_complete.len     = xferred_bytes;
+  event.xfer_complete.result  = result;
+
+  dcd_event_handler(&event, in_isr);
+}
+
 //--------------------------------------------------------------------+
 // Helper
 //--------------------------------------------------------------------+
+uint32_t usbd_control_xfer_st(uint8_t _rhport, uint8_t _dir, uint8_t* _buffer, uint16_t _len) {
+    uint32_t err = TUSB_ERROR_NONE;
+    if (_len) {
+      dcd_control_xfer(_rhport, _dir, (uint8_t*) _buffer, _len);
+    }
+
+    dcd_control_status(_rhport, _dir);
+    return err;
+}
+
 tusb_error_t usbd_open_edpt_pair(uint8_t rhport, tusb_desc_endpoint_t const* p_desc_ep, uint8_t xfer_type, uint8_t* ep_out, uint8_t* ep_in)
 {
   for(int i=0; i<2; i++)
diff --git a/src/device/usbd_pvt.h b/src/device/usbd_pvt.h
index 0b6b56d7d..ecb71f771 100644
--- a/src/device/usbd_pvt.h
+++ b/src/device/usbd_pvt.h
@@ -64,20 +64,7 @@ void         usbd_task (void* param);
 // helper to parse an pair of In and Out endpoint descriptors. They must be consecutive
 tusb_error_t usbd_open_edpt_pair(uint8_t rhport, tusb_desc_endpoint_t const* p_desc_ep, uint8_t xfer_type, uint8_t* ep_out, uint8_t* ep_in);
 
-// Carry out Data and Status stage of control transfer
-// Must be call in a subtask (_st) function
-#define usbd_control_xfer_st(_rhport, _dir, _buffer, _len)                    \
-  do {                                                                        \
-    if (_len) {                                                               \
-      uint32_t err;                                                           \
-      dcd_control_xfer(_rhport, _dir, (uint8_t*) _buffer, _len);              \
-      osal_semaphore_wait( _usbd_ctrl_sem, OSAL_TIMEOUT_CONTROL_XFER, &err ); \
-      STASK_ASSERT_ERR( err );                                                \
-    }                                                                         \
-    dcd_control_status(_rhport, _dir);                                        \
-    /* No need to wait for status phase to complete */                        \
-  }while(0)
-
+uint32_t usbd_control_xfer_st(uint8_t _rhport, uint8_t _dir, uint8_t* _buffer, uint16_t _len);
 
 /*------------------------------------------------------------------*/
 /* Other Helpers
diff --git a/src/osal/osal_freertos.h b/src/osal/osal_freertos.h
index ae988ec1f..f29759225 100644
--- a/src/osal/osal_freertos.h
+++ b/src/osal/osal_freertos.h
@@ -183,4 +183,3 @@ static inline void osal_queue_reset(osal_queue_t const queue_hdl)
 
 /** @} */
 /** @} */
-
diff --git a/src/osal/osal_none.h b/src/osal/osal_none.h
index 796ddc15f..6e67c8ea6 100644
--- a/src/osal/osal_none.h
+++ b/src/osal/osal_none.h
@@ -60,8 +60,6 @@
 //
 //   OSAL_TASK_LOOP_ENG
 // }
-//
-// NOTE: no switch statement is allowed in Task and subtask
 //--------------------------------------------------------------------+
 #define OSAL_TASK_DEF(_name, _str, _func, _prio, _stack_sz)  osal_task_def_t _name;
 
@@ -73,52 +71,8 @@ static inline bool osal_task_create(osal_task_def_t* taskdef)
   return true;
 }
 
-#define TASK_RESTART                             \
-  _state = 0
-
-#define osal_task_delay(_msec)                      \
-  do {                                              \
-    _timeout = tusb_hal_millis();                   \
-    _state = __LINE__; case __LINE__:               \
-      if ( _timeout + (_msec) > tusb_hal_millis() ) \
-        return TUSB_ERROR_OSAL_WAITING;             \
-  }while(0)
-
 //--------------------------------------------------------------------+
-// SUBTASK (a sub function that uses OS blocking services & called by a task
-//--------------------------------------------------------------------+
-#define OSAL_SUBTASK_BEGIN                       \
-  static uint16_t _state = 0;                    \
-  ATTR_UNUSED static uint32_t _timeout = 0;      \
-  (void) _timeout;                               \
-  switch(_state) {                               \
-    case 0: {
-
-#define OSAL_SUBTASK_END                         \
-  default: TASK_RESTART; break;                  \
-  }}                                             \
-  return TUSB_ERROR_NONE;
-
-#define STASK_INVOKE(_subtask, _status)                                         \
-  do {                                                                          \
-    _state = __LINE__; case __LINE__:                                           \
-    {                                                                           \
-      (_status) = _subtask; /* invoke sub task */                               \
-      if (TUSB_ERROR_OSAL_WAITING == (_status)) return TUSB_ERROR_OSAL_WAITING; \
-    }                                                                           \
-  }while(0)
-
-//------------- Sub Task Assert -------------//
-#define STASK_RETURN(error)     do { TASK_RESTART; return error; } while(0)
-
-#define STASK_ASSERT_ERR(_err)                TU_VERIFY_ERR_HDLR(_err, TU_BREAKPOINT(); TASK_RESTART, TUSB_ERROR_FAILED)
-#define STASK_ASSERT_ERR_HDLR(_err, _func)    TU_VERIFY_ERR_HDLR(_err, TU_BREAKPOINT(); _func; TASK_RESTART, TUSB_ERROR_FAILED )
-
-#define STASK_ASSERT(_cond)                   TU_VERIFY_HDLR(_cond, TU_BREAKPOINT(); TASK_RESTART, TUSB_ERROR_FAILED)
-#define STASK_ASSERT_HDLR(_cond, _func)       TU_VERIFY_HDLR(_cond, TU_BREAKPOINT(); _func; TASK_RESTART, TUSB_ERROR_FAILED)
-
-//--------------------------------------------------------------------+
-// Semaphore API
+// Binary Semaphore API
 //--------------------------------------------------------------------+
 typedef struct
 {
@@ -135,7 +89,6 @@ static inline osal_semaphore_t osal_semaphore_create(osal_semaphore_def_t* semde
 
 static inline bool osal_semaphore_post(osal_semaphore_t sem_hdl, bool in_isr)
 {
-  (void) in_isr;
   sem_hdl->count++;
   return true;
 }
@@ -145,22 +98,21 @@ static inline void osal_semaphore_reset(osal_semaphore_t sem_hdl)
   sem_hdl->count = 0;
 }
 
-#define osal_semaphore_wait(_sem_hdl, _msec, _err)                                               \
-  do {                                                                                           \
-    _timeout = tusb_hal_millis();                                                                \
-    _state = __LINE__; case __LINE__:                                                            \
-    if( (_sem_hdl)->count == 0 ) {                                                               \
-      if ( ((_msec) != OSAL_TIMEOUT_WAIT_FOREVER) && (_timeout + (_msec) <= tusb_hal_millis()) ) \
-        *(_err) = TUSB_ERROR_OSAL_TIMEOUT;                                                       \
-      else                                                                                       \
-        return TUSB_ERROR_OSAL_WAITING;                                                          \
-    } else{                                                                                      \
-      /* Enter critical ? */                                                                     \
-      (_sem_hdl)->count--;                                                                       \
-      /* Exit critical ? */                                                                      \
-      *(_err) = TUSB_ERROR_NONE;                                                                 \
-    }                                                                                            \
-  }while(0)
+static inline tusb_error_t osal_semaphore_wait(osal_semaphore_t sem_hdl, uint32_t msec) {
+  (void) msec;
+  while (true) {
+      while (sem_hdl->count == 0) {
+      }
+      // tusb_hal_int_disable_all();
+      if (sem_hdl->count == 0) {
+          sem_hdl->count--;
+          // tusb_hal_int_enable_all();
+          break;
+      }
+      // tusb_hal_int_enable_all();
+  }
+  return TUSB_ERROR_NONE;
+}
 
 //--------------------------------------------------------------------+
 // MUTEX API
@@ -218,22 +170,13 @@ static inline void osal_queue_reset(osal_queue_t const queue_hdl)
   queue_hdl->count = queue_hdl->rd_idx = queue_hdl->wr_idx = 0;
 }
 
-#define osal_queue_receive(_q_hdl, p_data, _msec, _err)                                           \
-  do {                                                                                            \
-    _timeout = tusb_hal_millis();                                                                 \
-    _state = __LINE__; case __LINE__:                                                             \
-    if( (_q_hdl)->count == 0 ) {                                                                  \
-      if ( ((_msec) != OSAL_TIMEOUT_WAIT_FOREVER) && ( _timeout + (_msec) <= tusb_hal_millis()) ) \
-        *(_err) = TUSB_ERROR_OSAL_TIMEOUT;                                                        \
-      else                                                                                        \
-        return TUSB_ERROR_OSAL_WAITING;                                                           \
-    } else{                                                                                       \
-      /* Enter critical ? */                                                                      \
-      tu_fifo_read(_q_hdl, p_data);                                                               \
-      /* Exit critical ? */                                                                       \
-      *(_err) = TUSB_ERROR_NONE;                                                                  \
-    }                                                                                             \
-  }while(0)
+static inline tusb_error_t osal_queue_receive(osal_queue_t const queue_hdl, void* data) {
+  if (!tu_fifo_read(queue_hdl, data)) {
+    return TUSB_ERROR_OSAL_WAITING;
+  }
+  return TUSB_ERROR_NONE;
+}
+
 
 #ifdef __cplusplus
  }
diff --git a/src/portable/microchip/samd21/dcd.c b/src/portable/microchip/samd21/dcd.c
new file mode 100644
index 000000000..c9e88ad17
--- /dev/null
+++ b/src/portable/microchip/samd21/dcd.c
@@ -0,0 +1,341 @@
+/**************************************************************************/
+/*!
+    @file     dcd_nrf5x.c
+    @author   hathach
+
+    @section LICENSE
+
+    Software License Agreement (BSD License)
+
+    Copyright (c) 2018, Scott Shawcroft for Adafruit Industries
+    All rights reserved.
+
+    Redistribution and use in source and binary forms, with or without
+    modification, are permitted provided that the following conditions are met:
+    1. Redistributions of source code must retain the above copyright
+    notice, this list of conditions and the following disclaimer.
+    2. Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in the
+    documentation and/or other materials provided with the distribution.
+    3. Neither the name of the copyright holders nor the
+    names of its contributors may be used to endorse or promote products
+    derived from this software without specific prior written permission.
+
+    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
+    EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+    WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+    DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
+    DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+    (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+    ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+    SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+    This file is part of the tinyusb stack.
+*/
+/**************************************************************************/
+
+#include "tusb_option.h"
+
+#if TUSB_OPT_DEVICE_ENABLED && CFG_TUSB_MCU == OPT_MCU_SAMD51
+
+#include "device/dcd.h"
+
+#include "device/usbd.h"
+#include "device/usbd_pvt.h" // to use defer function helper
+
+#include "class/msc/msc_device.h"
+
+#include "sam.h"
+
+/*------------------------------------------------------------------*/
+/* MACRO TYPEDEF CONSTANT ENUM
+ *------------------------------------------------------------------*/
+enum
+{
+  // Max allowed by USB specs
+  MAX_PACKET_SIZE   = 64,
+};
+
+UsbDeviceDescBank sram_registers[8][2];
+ATTR_ALIGNED(4) uint8_t control_out_buffer[64];
+ATTR_ALIGNED(4) uint8_t control_in_buffer[64];
+
+volatile uint32_t setup_count = 0;
+
+// Setup the control endpoint 0.
+static void bus_reset(void) {
+    // Max size of packets is 64 bytes.
+    UsbDeviceDescBank* bank_out = &sram_registers[0][TUSB_DIR_OUT];
+    bank_out->PCKSIZE.bit.SIZE = 0x3;
+    UsbDeviceDescBank* bank_in = &sram_registers[0][TUSB_DIR_IN];
+    bank_in->PCKSIZE.bit.SIZE = 0x3;
+
+    UsbDeviceEndpoint* ep = &USB->DEVICE.DeviceEndpoint[0];
+    ep->EPCFG.reg = USB_DEVICE_EPCFG_EPTYPE0(0x1) | USB_DEVICE_EPCFG_EPTYPE1(0x1);
+    ep->EPINTENSET.reg = USB_DEVICE_EPINTENSET_TRCPT0 | USB_DEVICE_EPINTENSET_TRCPT1 | USB_DEVICE_EPINTENSET_RXSTP;
+
+    dcd_edpt_xfer(0, 0, control_out_buffer, 64);
+    setup_count = 0;
+}
+
+
+/*------------------------------------------------------------------*/
+/* Controller API
+ *------------------------------------------------------------------*/
+bool dcd_init (uint8_t rhport)
+{
+  (void) rhport;
+  USB->DEVICE.DESCADD.reg = (uint32_t) &sram_registers;
+  USB->DEVICE.CTRLB.reg = USB_DEVICE_CTRLB_SPDCONF_FS;
+  USB->DEVICE.CTRLA.reg = USB_CTRLA_MODE_DEVICE | USB_CTRLA_ENABLE;
+  USB->DEVICE.INTENSET.reg = USB_DEVICE_INTENSET_SOF | USB_DEVICE_INTENSET_EORST;
+
+  return true;
+}
+
+void dcd_connect (uint8_t rhport)
+{
+
+}
+void dcd_disconnect (uint8_t rhport)
+{
+
+}
+
+void dcd_set_address (uint8_t rhport, uint8_t dev_addr)
+{
+  (void) rhport;
+  dcd_edpt_xfer (0, TUSB_DIR_IN_MASK, NULL, 0);
+  // Wait for EP0 to finish before switching the address.
+  while (USB->DEVICE.DeviceEndpoint[0].EPSTATUS.bit.BK1RDY == 1) {}
+  USB->DEVICE.DADD.reg = USB_DEVICE_DADD_DADD(dev_addr) | USB_DEVICE_DADD_ADDEN;
+}
+
+void dcd_set_config (uint8_t rhport, uint8_t config_num)
+{
+  (void) rhport;
+  (void) config_num;
+  // Nothing to do
+}
+
+/*------------------------------------------------------------------*/
+/* Control
+ *------------------------------------------------------------------*/
+
+bool dcd_control_xfer (uint8_t rhport, uint8_t dir, uint8_t * buffer, uint16_t length)
+{
+  (void) rhport;
+  uint8_t ep_addr = 0;
+  if (dir == TUSB_DIR_IN) {
+      ep_addr |= TUSB_DIR_IN_MASK;
+  }
+
+  return dcd_edpt_xfer (rhport, ep_addr, buffer, length);
+}
+
+bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt)
+{
+  (void) rhport;
+
+  uint8_t const epnum = edpt_number(desc_edpt->bEndpointAddress);
+  uint8_t const dir   = edpt_dir(desc_edpt->bEndpointAddress);
+
+  UsbDeviceDescBank* bank = &sram_registers[epnum][dir];
+  uint32_t size_value = 0;
+  while (size_value < 7) {
+    if (1 << (size_value + 3) == desc_edpt->wMaxPacketSize.size) {
+      break;
+    }
+    size_value++;
+  }
+  bank->PCKSIZE.bit.SIZE = size_value;
+
+  UsbDeviceEndpoint* ep = &USB->DEVICE.DeviceEndpoint[epnum];
+
+  if ( dir == TUSB_DIR_OUT )
+  {
+    ep->EPCFG.bit.EPTYPE0 = desc_edpt->bmAttributes.xfer + 1;
+    ep->EPINTENSET.bit.TRCPT0 = true;
+  }else
+  {
+    ep->EPCFG.bit.EPTYPE1 = desc_edpt->bmAttributes.xfer + 1;
+    ep->EPINTENSET.bit.TRCPT1 = true;
+  }
+  __ISB(); __DSB();
+
+  return true;
+}
+
+bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes)
+{
+  (void) rhport;
+
+  uint8_t const epnum = edpt_number(ep_addr);
+  uint8_t const dir   = edpt_dir(ep_addr);
+
+  UsbDeviceDescBank* bank = &sram_registers[epnum][dir];
+  UsbDeviceEndpoint* ep = &USB->DEVICE.DeviceEndpoint[epnum];
+
+  bank->ADDR.reg = (uint32_t) buffer;
+  if ( dir == TUSB_DIR_OUT )
+  {
+    bank->PCKSIZE.bit.MULTI_PACKET_SIZE = total_bytes;
+    bank->PCKSIZE.bit.BYTE_COUNT = 0;
+    ep->EPSTATUSCLR.reg |= USB_DEVICE_EPSTATUSCLR_BK0RDY;
+    ep->EPINTFLAG.reg |= USB_DEVICE_EPINTFLAG_TRFAIL0;
+  } else
+  {
+    bank->PCKSIZE.bit.MULTI_PACKET_SIZE = 0;
+    bank->PCKSIZE.bit.BYTE_COUNT = total_bytes;
+    // bank->PCKSIZE.bit.AUTO_ZLP = 1;
+    ep->EPSTATUSSET.reg |= USB_DEVICE_EPSTATUSSET_BK1RDY;
+    ep->EPINTFLAG.reg |= USB_DEVICE_EPINTFLAG_TRFAIL1;
+  }
+
+  return true;
+}
+
+bool dcd_edpt_stalled (uint8_t rhport, uint8_t ep_addr)
+{
+  (void) rhport;
+
+  // control is never got halted
+  if ( ep_addr == 0 ) {
+      return false;
+  }
+
+  uint8_t const epnum = edpt_number(ep_addr);
+  UsbDeviceEndpoint* ep = &USB->DEVICE.DeviceEndpoint[epnum];
+  return (edpt_dir(ep_addr) == TUSB_DIR_IN ) ? ep->EPINTFLAG.bit.STALL1 : ep->EPINTFLAG.bit.STALL0;
+}
+
+void dcd_edpt_stall (uint8_t rhport, uint8_t ep_addr)
+{
+  (void) rhport;
+
+  uint8_t const epnum = edpt_number(ep_addr);
+  UsbDeviceEndpoint* ep = &USB->DEVICE.DeviceEndpoint[epnum];
+
+  if (edpt_dir(ep_addr) == TUSB_DIR_IN) {
+      ep->EPSTATUSSET.reg = USB_DEVICE_EPSTATUSSET_STALLRQ1;
+  } else {
+      ep->EPSTATUSSET.reg = USB_DEVICE_EPSTATUSSET_STALLRQ0;
+  }
+
+  __ISB(); __DSB();
+}
+
+void dcd_edpt_clear_stall (uint8_t rhport, uint8_t ep_addr)
+{
+  (void) rhport;
+
+  uint8_t const epnum = edpt_number(ep_addr);
+  UsbDeviceEndpoint* ep = &USB->DEVICE.DeviceEndpoint[epnum];
+
+  if (edpt_dir(ep_addr) == TUSB_DIR_IN) {
+    ep->EPSTATUSCLR.reg = USB_DEVICE_EPSTATUSCLR_STALLRQ1;
+  } else {
+    ep->EPSTATUSCLR.reg = USB_DEVICE_EPSTATUSCLR_STALLRQ0;
+  }
+}
+
+bool dcd_edpt_busy (uint8_t rhport, uint8_t ep_addr)
+{
+  (void) rhport;
+
+  // USBD shouldn't check control endpoint state
+  if ( 0 == ep_addr ) return false;
+
+  uint8_t const epnum = edpt_number(ep_addr);
+  UsbDeviceEndpoint* ep = &USB->DEVICE.DeviceEndpoint[epnum];
+
+  if (edpt_dir(ep_addr) == TUSB_DIR_IN) {
+    return ep->EPINTFLAG.bit.TRCPT1 == 0 && ep->EPSTATUS.bit.BK1RDY == 1;
+  }
+  return ep->EPINTFLAG.bit.TRCPT0 == 0 && ep->EPSTATUS.bit.BK0RDY == 1;
+}
+
+/*------------------------------------------------------------------*/
+
+static bool maybe_handle_setup_packet(void) {
+    if (USB->DEVICE.DeviceEndpoint[0].EPINTFLAG.bit.RXSTP)
+    {
+        USB->DEVICE.DeviceEndpoint[0].EPINTFLAG.reg = USB_DEVICE_EPINTFLAG_RXSTP;
+
+        // This copies the data elsewhere so we can reuse the buffer.
+        dcd_event_setup_received(0, (uint8_t*) sram_registers[0][0].ADDR.reg, true);
+        dcd_edpt_xfer(0, 0, control_out_buffer, 64);
+        setup_count += 1;
+        return true;
+    }
+    return false;
+}
+
+void maybe_transfer_complete(void) {
+    uint32_t epints = USB->DEVICE.EPINTSMRY.reg;
+    for (uint8_t epnum = 0; epnum < USB_EPT_NUM; epnum++) {
+        if ((epints & (1 << epnum)) == 0) {
+            continue;
+        }
+
+        if (maybe_handle_setup_packet()) {
+            continue;
+        }
+        UsbDeviceEndpoint* ep = &USB->DEVICE.DeviceEndpoint[epnum];
+
+        uint32_t epintflag = ep->EPINTFLAG.reg;
+
+        // Handle IN completions
+        if ((epintflag & USB_DEVICE_EPINTFLAG_TRCPT1) != 0) {
+            ep->EPINTFLAG.reg = USB_DEVICE_EPINTFLAG_TRCPT1;
+
+            UsbDeviceDescBank* bank = &sram_registers[epnum][TUSB_DIR_IN];
+            uint16_t total_transfer_size = bank->PCKSIZE.bit.BYTE_COUNT;
+
+            uint8_t ep_addr = epnum | TUSB_DIR_IN_MASK;
+            dcd_event_xfer_complete(0, ep_addr, total_transfer_size, DCD_XFER_SUCCESS, true);
+        }
+
+        // Handle OUT completions
+        if ((epintflag & USB_DEVICE_EPINTFLAG_TRCPT0) != 0) {
+            ep->EPINTFLAG.reg = USB_DEVICE_EPINTFLAG_TRCPT0;
+
+            UsbDeviceDescBank* bank = &sram_registers[epnum][TUSB_DIR_OUT];
+            uint16_t total_transfer_size = bank->PCKSIZE.bit.BYTE_COUNT;
+
+            uint8_t ep_addr = epnum;
+            dcd_event_xfer_complete(0, ep_addr, total_transfer_size, DCD_XFER_SUCCESS, true);
+            if (epnum == 0) {
+                dcd_edpt_xfer(0, 0, control_out_buffer, 64);
+            }
+        }
+    }
+}
+
+void USB_Handler(void) {
+  uint32_t int_status = USB->DEVICE.INTFLAG.reg;
+
+  /*------------- Interrupt Processing -------------*/
+  if ( int_status & USB_DEVICE_INTFLAG_EORST )
+  {
+    USB->DEVICE.INTFLAG.reg = USB_DEVICE_INTENCLR_EORST;
+    bus_reset();
+    dcd_event_bus_signal(0, DCD_EVENT_BUS_RESET, true);
+  }
+
+  if ( int_status & USB_DEVICE_INTFLAG_SOF )
+  {
+    USB->DEVICE.INTFLAG.reg = USB_DEVICE_INTFLAG_SOF;
+    dcd_event_bus_signal(0, DCD_EVENT_SOF, true);
+  }
+
+  // Setup packet received.
+  maybe_handle_setup_packet();
+
+  // Handle complete transfer
+  maybe_transfer_complete();
+}
+
+#endif
diff --git a/src/portable/microchip/samd21/hal.c b/src/portable/microchip/samd21/hal.c
new file mode 100644
index 000000000..524840be2
--- /dev/null
+++ b/src/portable/microchip/samd21/hal.c
@@ -0,0 +1,82 @@
+/**************************************************************************/
+/*!
+    @file     hal_nrf5x.c
+    @author   hathach
+
+    @section LICENSE
+
+    Software License Agreement (BSD License)
+
+    Copyright (c) 2018, hathach (tinyusb.org)
+    All rights reserved.
+
+    Redistribution and use in source and binary forms, with or without
+    modification, are permitted provided that the following conditions are met:
+    1. Redistributions of source code must retain the above copyright
+    notice, this list of conditions and the following disclaimer.
+    2. Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in the
+    documentation and/or other materials provided with the distribution.
+    3. Neither the name of the copyright holders nor the
+    names of its contributors may be used to endorse or promote products
+    derived from this software without specific prior written permission.
+
+    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
+    EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+    WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+    DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
+    DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+    (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+    ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+    SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+    This file is part of the tinyusb stack.
+*/
+/**************************************************************************/
+
+#include "tusb_option.h"
+
+#if TUSB_OPT_DEVICE_ENABLED && CFG_TUSB_MCU == OPT_MCU_SAMD21
+
+#include "sam.h"
+
+#include "tusb_hal.h"
+
+/*------------------------------------------------------------------*/
+/* MACRO TYPEDEF CONSTANT ENUM
+ *------------------------------------------------------------------*/
+#define USB_NVIC_PRIO   7
+
+void tusb_hal_nrf_power_event(uint32_t event);
+
+/*------------------------------------------------------------------*/
+/* TUSB HAL
+ *------------------------------------------------------------------*/
+bool tusb_hal_init(void)
+{
+  USB->DEVICE.PADCAL.bit.TRANSP = (*((uint32_t*) USB_FUSES_TRANSP_ADDR) & USB_FUSES_TRANSP_Msk) >> USB_FUSES_TRANSP_Pos;
+  USB->DEVICE.PADCAL.bit.TRANSN = (*((uint32_t*) USB_FUSES_TRANSN_ADDR) & USB_FUSES_TRANSN_Msk) >> USB_FUSES_TRANSN_Pos;
+  USB->DEVICE.PADCAL.bit.TRIM = (*((uint32_t*) USB_FUSES_TRIM_ADDR) & USB_FUSES_TRIM_Msk) >> USB_FUSES_TRIM_Pos;
+
+  USB->DEVICE.QOSCTRL.bit.CQOS = USB_QOSCTRL_CQOS_HIGH_Val;
+  USB->DEVICE.QOSCTRL.bit.DQOS = USB_QOSCTRL_DQOS_HIGH_Val;
+
+  tusb_hal_int_enable(0);
+  return true;
+}
+
+void tusb_hal_int_enable(uint8_t rhport)
+{
+  (void) rhport;
+  NVIC_EnableIRQ(USB_IRQn);
+}
+
+void tusb_hal_int_disable(uint8_t rhport)
+{
+  (void) rhport;
+  NVIC_DisableIRQ(USB_IRQn);
+}
+
+#endif
diff --git a/src/portable/microchip/samd51/dcd.c b/src/portable/microchip/samd51/dcd.c
new file mode 100644
index 000000000..e67fbd295
--- /dev/null
+++ b/src/portable/microchip/samd51/dcd.c
@@ -0,0 +1,360 @@
+/**************************************************************************/
+/*!
+    @file     dcd_nrf5x.c
+    @author   hathach
+
+    @section LICENSE
+
+    Software License Agreement (BSD License)
+
+    Copyright (c) 2018, Scott Shawcroft for Adafruit Industries
+    All rights reserved.
+
+    Redistribution and use in source and binary forms, with or without
+    modification, are permitted provided that the following conditions are met:
+    1. Redistributions of source code must retain the above copyright
+    notice, this list of conditions and the following disclaimer.
+    2. Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in the
+    documentation and/or other materials provided with the distribution.
+    3. Neither the name of the copyright holders nor the
+    names of its contributors may be used to endorse or promote products
+    derived from this software without specific prior written permission.
+
+    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
+    EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+    WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+    DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
+    DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+    (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+    ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+    SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+    This file is part of the tinyusb stack.
+*/
+/**************************************************************************/
+
+#include "tusb_option.h"
+
+#if TUSB_OPT_DEVICE_ENABLED && CFG_TUSB_MCU == OPT_MCU_SAMD51
+
+#include "device/dcd.h"
+
+#include "device/usbd.h"
+#include "device/usbd_pvt.h" // to use defer function helper
+
+#include "sam.h"
+
+/*------------------------------------------------------------------*/
+/* MACRO TYPEDEF CONSTANT ENUM
+ *------------------------------------------------------------------*/
+enum
+{
+  // Max allowed by USB specs
+  MAX_PACKET_SIZE   = 64,
+};
+
+UsbDeviceDescBank sram_registers[8][2];
+ATTR_ALIGNED(4) uint8_t control_out_buffer[64];
+ATTR_ALIGNED(4) uint8_t control_in_buffer[64];
+
+volatile uint32_t setup_count = 0;
+
+// Setup the control endpoint 0.
+static void bus_reset(void) {
+    // Max size of packets is 64 bytes.
+    UsbDeviceDescBank* bank_out = &sram_registers[0][TUSB_DIR_OUT];
+    bank_out->PCKSIZE.bit.SIZE = 0x3;
+    UsbDeviceDescBank* bank_in = &sram_registers[0][TUSB_DIR_IN];
+    bank_in->PCKSIZE.bit.SIZE = 0x3;
+
+    UsbDeviceEndpoint* ep = &USB->DEVICE.DeviceEndpoint[0];
+    ep->EPCFG.reg = USB_DEVICE_EPCFG_EPTYPE0(0x1) | USB_DEVICE_EPCFG_EPTYPE1(0x1);
+    ep->EPINTENSET.reg = USB_DEVICE_EPINTENSET_TRCPT0 | USB_DEVICE_EPINTENSET_TRCPT1 | USB_DEVICE_EPINTENSET_RXSTP;
+
+    dcd_edpt_xfer(0, 0, control_out_buffer, 64);
+    setup_count = 0;
+}
+
+
+/*------------------------------------------------------------------*/
+/* Controller API
+ *------------------------------------------------------------------*/
+bool dcd_init (uint8_t rhport)
+{
+  (void) rhport;
+  USB->DEVICE.DESCADD.reg = (uint32_t) &sram_registers;
+  USB->DEVICE.CTRLB.reg = USB_DEVICE_CTRLB_SPDCONF_FS;
+  USB->DEVICE.CTRLA.reg = USB_CTRLA_MODE_DEVICE | USB_CTRLA_ENABLE;
+  USB->DEVICE.INTENSET.reg = USB_DEVICE_INTENSET_SOF | USB_DEVICE_INTENSET_EORST;
+
+  return true;
+}
+
+void dcd_connect (uint8_t rhport)
+{
+
+}
+void dcd_disconnect (uint8_t rhport)
+{
+
+}
+
+void dcd_set_address (uint8_t rhport, uint8_t dev_addr)
+{
+  (void) rhport;
+  dcd_edpt_xfer (0, TUSB_DIR_IN_MASK, NULL, 0);
+  // Wait for EP0 to finish before switching the address.
+  while (USB->DEVICE.DeviceEndpoint[0].EPSTATUS.bit.BK1RDY == 1) {}
+  USB->DEVICE.DADD.reg = USB_DEVICE_DADD_DADD(dev_addr) | USB_DEVICE_DADD_ADDEN;
+}
+
+void dcd_set_config (uint8_t rhport, uint8_t config_num)
+{
+  (void) rhport;
+  (void) config_num;
+  // Nothing to do
+}
+
+/*------------------------------------------------------------------*/
+/* Control
+ *------------------------------------------------------------------*/
+
+bool dcd_control_xfer (uint8_t rhport, uint8_t dir, uint8_t * buffer, uint16_t length)
+{
+  (void) rhport;
+  uint8_t ep_addr = 0;
+  if (dir == TUSB_DIR_IN) {
+      ep_addr |= TUSB_DIR_IN_MASK;
+  }
+
+  return dcd_edpt_xfer (rhport, ep_addr, buffer, length);
+}
+
+bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt)
+{
+  (void) rhport;
+
+  uint8_t const epnum = edpt_number(desc_edpt->bEndpointAddress);
+  uint8_t const dir   = edpt_dir(desc_edpt->bEndpointAddress);
+
+  UsbDeviceDescBank* bank = &sram_registers[epnum][dir];
+  uint32_t size_value = 0;
+  while (size_value < 7) {
+    if (1 << (size_value + 3) == desc_edpt->wMaxPacketSize.size) {
+      break;
+    }
+    size_value++;
+  }
+  bank->PCKSIZE.bit.SIZE = size_value;
+
+  UsbDeviceEndpoint* ep = &USB->DEVICE.DeviceEndpoint[epnum];
+
+  if ( dir == TUSB_DIR_OUT )
+  {
+    ep->EPCFG.bit.EPTYPE0 = desc_edpt->bmAttributes.xfer + 1;
+    ep->EPINTENSET.bit.TRCPT0 = true;
+  }else
+  {
+    ep->EPCFG.bit.EPTYPE1 = desc_edpt->bmAttributes.xfer + 1;
+    ep->EPINTENSET.bit.TRCPT1 = true;
+  }
+  __ISB(); __DSB();
+
+  return true;
+}
+
+bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes)
+{
+  (void) rhport;
+
+  uint8_t const epnum = edpt_number(ep_addr);
+  uint8_t const dir   = edpt_dir(ep_addr);
+
+  UsbDeviceDescBank* bank = &sram_registers[epnum][dir];
+  UsbDeviceEndpoint* ep = &USB->DEVICE.DeviceEndpoint[epnum];
+
+  bank->ADDR.reg = (uint32_t) buffer;
+  if ( dir == TUSB_DIR_OUT )
+  {
+    bank->PCKSIZE.bit.MULTI_PACKET_SIZE = total_bytes;
+    bank->PCKSIZE.bit.BYTE_COUNT = 0;
+    ep->EPSTATUSCLR.reg |= USB_DEVICE_EPSTATUSCLR_BK0RDY;
+    ep->EPINTFLAG.reg |= USB_DEVICE_EPINTFLAG_TRFAIL0;
+  } else
+  {
+    bank->PCKSIZE.bit.MULTI_PACKET_SIZE = 0;
+    bank->PCKSIZE.bit.BYTE_COUNT = total_bytes;
+    ep->EPSTATUSSET.reg |= USB_DEVICE_EPSTATUSSET_BK1RDY;
+    ep->EPINTFLAG.reg |= USB_DEVICE_EPINTFLAG_TRFAIL1;
+  }
+
+  return true;
+}
+
+bool dcd_edpt_stalled (uint8_t rhport, uint8_t ep_addr)
+{
+  (void) rhport;
+
+  // control is never got halted
+  if ( ep_addr == 0 ) {
+      return false;
+  }
+
+  uint8_t const epnum = edpt_number(ep_addr);
+  UsbDeviceEndpoint* ep = &USB->DEVICE.DeviceEndpoint[epnum];
+  return (edpt_dir(ep_addr) == TUSB_DIR_IN ) ? ep->EPINTFLAG.bit.STALL1 : ep->EPINTFLAG.bit.STALL0;
+}
+
+void dcd_edpt_stall (uint8_t rhport, uint8_t ep_addr)
+{
+  (void) rhport;
+
+  uint8_t const epnum = edpt_number(ep_addr);
+  UsbDeviceEndpoint* ep = &USB->DEVICE.DeviceEndpoint[epnum];
+
+  if (edpt_dir(ep_addr) == TUSB_DIR_IN) {
+      ep->EPSTATUSSET.reg = USB_DEVICE_EPSTATUSSET_STALLRQ1;
+  } else {
+      ep->EPSTATUSSET.reg = USB_DEVICE_EPSTATUSSET_STALLRQ0;
+  }
+
+  __ISB(); __DSB();
+}
+
+void dcd_edpt_clear_stall (uint8_t rhport, uint8_t ep_addr)
+{
+  (void) rhport;
+
+  uint8_t const epnum = edpt_number(ep_addr);
+  UsbDeviceEndpoint* ep = &USB->DEVICE.DeviceEndpoint[epnum];
+
+  if (edpt_dir(ep_addr) == TUSB_DIR_IN) {
+    ep->EPSTATUSCLR.reg = USB_DEVICE_EPSTATUSCLR_STALLRQ1;
+  } else {
+    ep->EPSTATUSCLR.reg = USB_DEVICE_EPSTATUSCLR_STALLRQ0;
+  }
+}
+
+bool dcd_edpt_busy (uint8_t rhport, uint8_t ep_addr)
+{
+  (void) rhport;
+
+  // USBD shouldn't check control endpoint state
+  if ( 0 == ep_addr ) return false;
+
+  uint8_t const epnum = edpt_number(ep_addr);
+  UsbDeviceEndpoint* ep = &USB->DEVICE.DeviceEndpoint[epnum];
+
+  if (edpt_dir(ep_addr) == TUSB_DIR_IN) {
+    return ep->EPINTFLAG.bit.TRCPT1 == 0 && ep->EPSTATUS.bit.BK1RDY == 1;
+  }
+  return ep->EPINTFLAG.bit.TRCPT0 == 0 && ep->EPSTATUS.bit.BK0RDY == 1;
+}
+
+/*------------------------------------------------------------------*/
+
+static bool maybe_handle_setup_packet(void) {
+    if (USB->DEVICE.DeviceEndpoint[0].EPINTFLAG.bit.RXSTP)
+    {
+        USB->DEVICE.DeviceEndpoint[0].EPINTFLAG.reg = USB_DEVICE_EPINTFLAG_RXSTP;
+        // uint8_t* buf = (uint8_t*) sram_registers[0][0].ADDR.reg;
+        //
+        // if (buf[6] == 0x12) asm("bkpt");
+        // This copies the data elsewhere so we can reuse the buffer.
+        dcd_event_setup_received(0, (uint8_t*) sram_registers[0][0].ADDR.reg, true);
+        dcd_edpt_xfer(0, 0, control_out_buffer, 64);
+        setup_count += 1;
+        return true;
+    }
+    return false;
+}
+/*
+ *------------------------------------------------------------------*/
+/* USB_EORSM_DNRSM, USB_EORST_RST, USB_LPMSUSP_DDISC, USB_LPM_DCONN,
+USB_MSOF, USB_RAMACER, USB_RXSTP_TXSTP_0, USB_RXSTP_TXSTP_1,
+USB_RXSTP_TXSTP_2, USB_RXSTP_TXSTP_3, USB_RXSTP_TXSTP_4,
+USB_RXSTP_TXSTP_5, USB_RXSTP_TXSTP_6, USB_RXSTP_TXSTP_7,
+USB_STALL0_STALL_0, USB_STALL0_STALL_1, USB_STALL0_STALL_2,
+USB_STALL0_STALL_3, USB_STALL0_STALL_4, USB_STALL0_STALL_5,
+USB_STALL0_STALL_6, USB_STALL0_STALL_7, USB_STALL1_0, USB_STALL1_1,
+USB_STALL1_2, USB_STALL1_3, USB_STALL1_4, USB_STALL1_5, USB_STALL1_6,
+USB_STALL1_7, USB_SUSPEND, USB_TRFAIL0_TRFAIL_0, USB_TRFAIL0_TRFAIL_1,
+USB_TRFAIL0_TRFAIL_2, USB_TRFAIL0_TRFAIL_3, USB_TRFAIL0_TRFAIL_4,
+USB_TRFAIL0_TRFAIL_5, USB_TRFAIL0_TRFAIL_6, USB_TRFAIL0_TRFAIL_7,
+USB_TRFAIL1_PERR_0, USB_TRFAIL1_PERR_1, USB_TRFAIL1_PERR_2,
+USB_TRFAIL1_PERR_3, USB_TRFAIL1_PERR_4, USB_TRFAIL1_PERR_5,
+USB_TRFAIL1_PERR_6, USB_TRFAIL1_PERR_7, USB_UPRSM, USB_WAKEUP */
+void USB_0_Handler(void) {
+  uint32_t int_status = USB->DEVICE.INTFLAG.reg;
+
+  /*------------- Interrupt Processing -------------*/
+  if ( int_status & USB_DEVICE_INTFLAG_EORST )
+  {
+    USB->DEVICE.INTFLAG.reg = USB_DEVICE_INTENCLR_EORST;
+    bus_reset();
+    dcd_event_bus_signal(0, DCD_EVENT_BUS_RESET, true);
+  }
+
+  // Setup packet received.
+  maybe_handle_setup_packet();
+}
+/* USB_SOF_HSOF */
+void USB_1_Handler(void) {
+    USB->DEVICE.INTFLAG.reg = USB_DEVICE_INTFLAG_SOF;
+    dcd_event_bus_signal(0, DCD_EVENT_SOF, true);
+}
+
+void transfer_complete(uint8_t direction) {
+        // uint8_t* buf = (uint8_t*) sram_registers[0][0].ADDR.reg;
+        //
+        // if (buf[6] == 0x12 || setup_count == 2) asm("bkpt");
+    uint32_t epints = USB->DEVICE.EPINTSMRY.reg;
+    for (uint8_t epnum = 0; epnum < USB_EPT_NUM; epnum++) {
+        if ((epints & (1 << epnum)) == 0) {
+            continue;
+        }
+
+        if (direction == TUSB_DIR_OUT && maybe_handle_setup_packet()) {
+            continue;
+        }
+        UsbDeviceEndpoint* ep = &USB->DEVICE.DeviceEndpoint[epnum];
+
+        UsbDeviceDescBank* bank = &sram_registers[epnum][direction];
+        uint16_t total_transfer_size = bank->PCKSIZE.bit.BYTE_COUNT;
+
+        uint8_t ep_addr = epnum;
+        if (direction == TUSB_DIR_IN) {
+            ep_addr |= TUSB_DIR_IN_MASK;
+        }
+        dcd_event_xfer_complete(0, ep_addr, total_transfer_size, DCD_XFER_SUCCESS, true);
+        if (epnum == 0 && direction == TUSB_DIR_OUT) {
+            dcd_edpt_xfer(0, 0, control_out_buffer, 64);
+        }
+        if (direction == TUSB_DIR_IN) {
+            ep->EPINTFLAG.reg = USB_DEVICE_EPINTFLAG_TRCPT1;
+        } else {
+            ep->EPINTFLAG.reg = USB_DEVICE_EPINTFLAG_TRCPT0;
+        }
+    }
+}
+
+// Bank zero is for OUT and SETUP transactions.
+/* USB_TRCPT0_0, USB_TRCPT0_1, USB_TRCPT0_2,
+USB_TRCPT0_3, USB_TRCPT0_4, USB_TRCPT0_5,
+USB_TRCPT0_6, USB_TRCPT0_7 */
+void USB_2_Handler(void) {
+    transfer_complete(TUSB_DIR_OUT);
+}
+
+// Bank one is used for IN transactions.
+/* USB_TRCPT1_0, USB_TRCPT1_1, USB_TRCPT1_2,
+USB_TRCPT1_3, USB_TRCPT1_4, USB_TRCPT1_5,
+USB_TRCPT1_6, USB_TRCPT1_7 */
+void USB_3_Handler(void) {
+    transfer_complete(TUSB_DIR_IN);
+}
+
+#endif
diff --git a/src/portable/microchip/samd51/hal.c b/src/portable/microchip/samd51/hal.c
new file mode 100644
index 000000000..d8c71a7fe
--- /dev/null
+++ b/src/portable/microchip/samd51/hal.c
@@ -0,0 +1,88 @@
+/**************************************************************************/
+/*!
+    @file     hal_nrf5x.c
+    @author   hathach
+
+    @section LICENSE
+
+    Software License Agreement (BSD License)
+
+    Copyright (c) 2018, hathach (tinyusb.org)
+    All rights reserved.
+
+    Redistribution and use in source and binary forms, with or without
+    modification, are permitted provided that the following conditions are met:
+    1. Redistributions of source code must retain the above copyright
+    notice, this list of conditions and the following disclaimer.
+    2. Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in the
+    documentation and/or other materials provided with the distribution.
+    3. Neither the name of the copyright holders nor the
+    names of its contributors may be used to endorse or promote products
+    derived from this software without specific prior written permission.
+
+    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
+    EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+    WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+    DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
+    DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+    (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+    ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+    SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+    This file is part of the tinyusb stack.
+*/
+/**************************************************************************/
+
+#include "tusb_option.h"
+
+#if TUSB_OPT_DEVICE_ENABLED && CFG_TUSB_MCU == OPT_MCU_SAMD51
+
+#include "sam.h"
+
+#include "tusb_hal.h"
+
+/*------------------------------------------------------------------*/
+/* MACRO TYPEDEF CONSTANT ENUM
+ *------------------------------------------------------------------*/
+#define USB_NVIC_PRIO   7
+
+void tusb_hal_nrf_power_event(uint32_t event);
+
+/*------------------------------------------------------------------*/
+/* TUSB HAL
+ *------------------------------------------------------------------*/
+bool tusb_hal_init(void)
+{
+  USB->DEVICE.PADCAL.bit.TRANSP = (*((uint32_t*) USB_FUSES_TRANSP_ADDR) & USB_FUSES_TRANSP_Msk) >> USB_FUSES_TRANSP_Pos;
+  USB->DEVICE.PADCAL.bit.TRANSN = (*((uint32_t*) USB_FUSES_TRANSN_ADDR) & USB_FUSES_TRANSN_Msk) >> USB_FUSES_TRANSN_Pos;
+  USB->DEVICE.PADCAL.bit.TRIM = (*((uint32_t*) USB_FUSES_TRIM_ADDR) & USB_FUSES_TRIM_Msk) >> USB_FUSES_TRIM_Pos;
+
+  USB->DEVICE.QOSCTRL.bit.CQOS = 3;
+  USB->DEVICE.QOSCTRL.bit.DQOS = 3;
+
+  tusb_hal_int_enable(0);
+  return true;
+}
+
+void tusb_hal_int_enable(uint8_t rhport)
+{
+  (void) rhport;
+  NVIC_EnableIRQ(USB_0_IRQn);
+  NVIC_EnableIRQ(USB_1_IRQn);
+  NVIC_EnableIRQ(USB_2_IRQn);
+  NVIC_EnableIRQ(USB_3_IRQn);
+}
+
+void tusb_hal_int_disable(uint8_t rhport)
+{
+  (void) rhport;
+  NVIC_DisableIRQ(USB_3_IRQn);
+  NVIC_DisableIRQ(USB_2_IRQn);
+  NVIC_DisableIRQ(USB_1_IRQn);
+  NVIC_DisableIRQ(USB_0_IRQn);
+}
+
+#endif
diff --git a/src/portable/nordic/nrf5x/dcd_nrf5x.c b/src/portable/nordic/nrf5x/dcd_nrf5x.c
index 69e001240..7eeab1306 100644
--- a/src/portable/nordic/nrf5x/dcd_nrf5x.c
+++ b/src/portable/nordic/nrf5x/dcd_nrf5x.c
@@ -203,7 +203,9 @@ static void xact_control_start(void)
 
 bool dcd_control_xfer (uint8_t rhport, uint8_t dir, uint8_t * buffer, uint16_t length)
 {
+
   (void) rhport;
+  osal_semaphore_wait( _usbd_ctrl_sem, OSAL_TIMEOUT_CONTROL_XFER);
 
   if ( length )
   {
@@ -216,9 +218,11 @@ bool dcd_control_xfer (uint8_t rhport, uint8_t dir, uint8_t * buffer, uint16_t l
     xact_control_start();
   }else
   {
+    NRF_USBD->EPIN[0].PTR        = 0;
+    NRF_USBD->EPIN[0].MAXCNT     = 0;
     // Status Phase also require Easy DMA has to be free as well !!!!
-    edpt_dma_start(&NRF_USBD->TASKS_EP0STATUS);
-    edpt_dma_end();
+    NRF_USBD->TASKS_EP0STATUS = 1;
+    osal_semaphore_post(_usbd_ctrl_sem, false);
   }
 
   return true;
@@ -434,7 +438,7 @@ void USBD_IRQHandler(void)
         NRF_USBD->BMREQUESTTYPE , NRF_USBD->BREQUEST, NRF_USBD->WVALUEL , NRF_USBD->WVALUEH,
         NRF_USBD->WINDEXL       , NRF_USBD->WINDEXH , NRF_USBD->WLENGTHL, NRF_USBD->WLENGTHH
     };
-    dcd_event_setup_recieved(0, setup, true);
+    dcd_event_setup_received(0, setup, true);
   }
 
   if ( int_status & USBD_INTEN_EP0DATADONE_Msk )
diff --git a/src/tusb_option.h b/src/tusb_option.h
index 846367129..d7a6c6fe0 100644
--- a/src/tusb_option.h
+++ b/src/tusb_option.h
@@ -147,6 +147,8 @@
 
   #ifndef CFG_TUD_ENUM_BUFFER_SIZE
     #define CFG_TUD_CTRL_BUFSIZE 256
+  #else
+    #define CFG_TUD_CTRL_BUFSIZE CFG_TUD_ENUM_BUFFER_SIZE
   #endif
 
   #ifndef CFG_TUD_DESC_AUTO