diff --git a/src/class/cdc/cdc.h b/src/class/cdc/cdc.h
index 1b127ad28..afac8379d 100644
--- a/src/class/cdc/cdc.h
+++ b/src/class/cdc/cdc.h
@@ -50,6 +50,10 @@
  extern "C" {
 #endif
 
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wpacked"
+#pragma GCC diagnostic ignored "-Wattributes"
+
 /** \defgroup ClassDriver_CDC_Common Common Definitions
  *  @{ */
 
@@ -86,7 +90,8 @@ typedef enum
 /// Communication Interface Protocol Codes
 typedef enum
 {
-  CDC_COMM_PROTOCOL_ATCOMMAND              = 0x01 , ///< AT Commands: V.250 etc
+  CDC_COMM_PROTOCOL_NONE                   = 0x00 , ///< No specific protocol
+  CDC_COMM_PROTOCOL_ATCOMMAND                     , ///< AT Commands: V.250 etc
   CDC_COMM_PROTOCOL_ATCOMMAND_PCCA_101            , ///< AT Commands defined by PCCA-101
   CDC_COMM_PROTOCOL_ATCOMMAND_PCCA_101_AND_ANNEXO , ///< AT Commands defined by PCCA-101 & Annex O
   CDC_COMM_PROTOCOL_ATCOMMAND_GSM_707             , ///< AT Commands defined by GSM 07.07
@@ -401,6 +406,8 @@ typedef struct ATTR_PACKED
 
 TU_VERIFY_STATIC(sizeof(cdc_line_control_state_t) == 2, "size is not correct");
 
+#pragma GCC diagnostic pop
+
 /** @} */
 
 #ifdef __cplusplus
diff --git a/src/class/cdc/cdc_device.c b/src/class/cdc/cdc_device.c
index 392ad349d..bc16699f1 100644
--- a/src/class/cdc/cdc_device.c
+++ b/src/class/cdc/cdc_device.c
@@ -45,6 +45,7 @@
 // INCLUDE
 //--------------------------------------------------------------------+
 #include "cdc_device.h"
+#include "device/control.h"
 #include "device/usbd_pvt.h"
 
 //--------------------------------------------------------------------+
@@ -235,8 +236,10 @@ tusb_error_t cdcd_open(uint8_t rhport, tusb_desc_interface_t const * p_interface
 {
   if ( CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL != p_interface_desc->bInterfaceSubClass) return TUSB_ERROR_CDC_UNSUPPORTED_SUBCLASS;
 
+  // Only support AT commands, no protocol and vendor specific commands.
   if ( !(tu_within(CDC_COMM_PROTOCOL_ATCOMMAND, p_interface_desc->bInterfaceProtocol, CDC_COMM_PROTOCOL_ATCOMMAND_CDMA) ||
-         0xff == p_interface_desc->bInterfaceProtocol) )
+         p_interface_desc->bInterfaceProtocol == CDC_COMM_PROTOCOL_NONE ||
+         p_interface_desc->bInterfaceProtocol == 0xff ) )
   {
     return TUSB_ERROR_CDC_UNSUPPORTED_PROTOCOL;
   }
@@ -296,10 +299,23 @@ tusb_error_t cdcd_open(uint8_t rhport, tusb_desc_interface_t const * p_interface
   return TUSB_ERROR_NONE;
 }
 
-tusb_error_t cdcd_control_request_st(uint8_t rhport, tusb_control_request_t const * p_request)
+void cdcd_control_request_complete(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;
 
+  // TODO Support multiple interfaces
+  uint8_t const itf = 0;
+  cdcd_interface_t* p_cdc = &_cdcd_itf[itf];
+
+  // Invoke callback
+  if (CDC_REQUEST_SET_LINE_CODING == p_request->bRequest) {
+    if ( tud_cdc_line_coding_cb ) tud_cdc_line_coding_cb(itf, &p_cdc->line_coding);
+  }
+}
+
+tusb_error_t cdcd_control_request(uint8_t rhport, tusb_control_request_t const * p_request, uint16_t bytes_already_sent)
+{
   //------------- Class Specific Request -------------//
   if (p_request->bmRequestType_bit.type != TUSB_REQ_TYPE_CLASS) return TUSB_ERROR_DCD_CONTROL_REQUEST_NOT_SUPPORT;
 
@@ -307,21 +323,18 @@ tusb_error_t cdcd_control_request_st(uint8_t rhport, tusb_control_request_t cons
   uint8_t const itf = 0;
   cdcd_interface_t* p_cdc = &_cdcd_itf[itf];
 
-  if ( (CDC_REQUEST_GET_LINE_CODING == p_request->bRequest) || (CDC_REQUEST_SET_LINE_CODING == p_request->bRequest) )
+  if ((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);
-
-    // Invoke callback
-    if (CDC_REQUEST_SET_LINE_CODING == p_request->bRequest)
-    {
-      if ( tud_cdc_line_coding_cb ) tud_cdc_line_coding_cb(itf, &p_cdc->line_coding);
-    }
+    dcd_edpt_xfer(rhport, 0, (uint8_t*) &p_cdc->line_coding, len);
+  }
+  else if ( (CDC_REQUEST_GET_LINE_CODING == p_request->bRequest))
+  {
+    uint16_t len = tu_min16(sizeof(cdc_line_coding_t), p_request->wLength);
+    dcd_edpt_xfer(rhport, TUSB_DIR_IN_MASK, (uint8_t*) &p_cdc->line_coding, len);
   }
   else if (CDC_REQUEST_SET_CONTROL_LINE_STATE == p_request->bRequest )
   {
-    dcd_control_status(rhport, p_request->bmRequestType_bit.direction); // ACK control request
-
     // CDC PSTN v1.2 section 6.3.12
     // Bit 0: Indicates if DTE is present or not.
     //        This signal corresponds to V.24 signal 108/2 and RS-232 signal DTR (Data Terminal Ready)
@@ -334,10 +347,9 @@ tusb_error_t cdcd_control_request_st(uint8_t rhport, tusb_control_request_t cons
   }
   else
   {
-    dcd_control_stall(rhport); // stall unsupported request
+    return TUSB_ERROR_FAILED; // 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/cdc/cdc_device.h b/src/class/cdc/cdc_device.h
index f1bd33953..9d0b93c6d 100644
--- a/src/class/cdc/cdc_device.h
+++ b/src/class/cdc/cdc_device.h
@@ -114,7 +114,8 @@ ATTR_WEAK void tud_cdc_line_coding_cb(uint8_t itf, cdc_line_coding_t const* p_li
 
 void         cdcd_init               (void);
 tusb_error_t cdcd_open               (uint8_t rhport, tusb_desc_interface_t const * p_interface_desc, uint16_t *p_length);
-tusb_error_t cdcd_control_request_st (uint8_t rhport, tusb_control_request_t const * p_request);
+tusb_error_t cdcd_control_request (uint8_t rhport, tusb_control_request_t const * p_request, uint16_t bytes_already_sent);
+void cdcd_control_request_complete (uint8_t rhport, tusb_control_request_t const * p_request);
 tusb_error_t cdcd_xfer_cb            (uint8_t rhport, uint8_t edpt_addr, tusb_event_t event, uint32_t xferred_bytes);
 void         cdcd_reset              (uint8_t rhport);
 
diff --git a/src/class/custom/custom_device.c b/src/class/custom/custom_device.c
index d43a86b14..1b36f1331 100644
--- a/src/class/custom/custom_device.c
+++ b/src/class/custom/custom_device.c
@@ -89,7 +89,7 @@ tusb_error_t cusd_open(uint8_t rhport, tusb_desc_interface_t const * p_desc_itf,
   return TUSB_ERROR_NONE;
 }
 
-tusb_error_t cusd_control_request_st(uint8_t rhport, tusb_control_request_t const * p_request)
+tusb_error_t cusd_control_request(uint8_t rhport, tusb_control_request_t const * p_request)
 {
   return TUSB_ERROR_DCD_CONTROL_REQUEST_NOT_SUPPORT;
 }
diff --git a/src/class/custom/custom_device.h b/src/class/custom/custom_device.h
index 81a074ce3..116c6897d 100644
--- a/src/class/custom/custom_device.h
+++ b/src/class/custom/custom_device.h
@@ -65,6 +65,7 @@
 void cusd_init(void);
 tusb_error_t cusd_open(uint8_t rhport, tusb_desc_interface_t const * p_interface_desc, uint16_t *p_length);
 tusb_error_t cusd_control_request_st(uint8_t rhport, tusb_control_request_t const * p_request);
+void cusd_control_request_complete (uint8_t rhport, tusb_control_request_t const * p_request);
 tusb_error_t cusd_xfer_cb(uint8_t rhport, uint8_t edpt_addr, tusb_event_t event, uint32_t xferred_bytes);
 void cusd_reset(uint8_t rhport);
 #endif
diff --git a/src/class/hid/hid.h b/src/class/hid/hid.h
index d2803177b..0008e47fb 100644
--- a/src/class/hid/hid.h
+++ b/src/class/hid/hid.h
@@ -49,6 +49,10 @@
  extern "C" {
 #endif
 
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wpacked"
+#pragma GCC diagnostic ignored "-Wattributes"
+
 //--------------------------------------------------------------------+
 // Common Definitions
 //--------------------------------------------------------------------+
@@ -609,6 +613,7 @@ enum
   HID_USAGE_CONSUMER_AC_PAN                            = 0x0238,
 };
 
+#pragma GCC diagnostic pop
 
 #ifdef __cplusplus
  }
diff --git a/src/class/hid/hid_device.c b/src/class/hid/hid_device.c
index d8696f137..efa602ad6 100644
--- a/src/class/hid/hid_device.c
+++ b/src/class/hid/hid_device.c
@@ -46,6 +46,7 @@
 //--------------------------------------------------------------------+
 #include "common/tusb_common.h"
 #include "hid_device.h"
+#include "device/control.h"
 #include "device/usbd_pvt.h"
 
 //--------------------------------------------------------------------+
@@ -402,13 +403,11 @@ tusb_error_t hidd_open(uint8_t rhport, tusb_desc_interface_t const * desc_itf, u
   return TUSB_ERROR_NONE;
 }
 
-tusb_error_t hidd_control_request_st(uint8_t rhport, tusb_control_request_t const * p_request)
+tusb_error_t hidd_control_request(uint8_t rhport, tusb_control_request_t const * p_request, uint16_t bytes_already_sent)
 {
   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)
   {
@@ -418,14 +417,17 @@ 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 );
-      memcpy(_usbd_ctrl_buf, p_hid->desc_report, p_hid->desc_len);
+      // TODO: Handle zero length packet.
+      uint16_t remaining_bytes = p_hid->desc_len - bytes_already_sent;
+      if (remaining_bytes > 64) {
+          remaining_bytes = 64;
+      }
+      memcpy(_shared_control_buffer, p_hid->desc_report + bytes_already_sent, remaining_bytes);
 
-      usbd_control_xfer_st(rhport, p_request->bmRequestType_bit.direction, _usbd_ctrl_buf, p_hid->desc_len);
+      dcd_edpt_xfer(rhport, TUSB_DIR_IN_MASK, _shared_control_buffer, remaining_bytes);
     }else
     {
-      dcd_control_stall(rhport);
+      return TUSB_ERROR_FAILED;
     }
   }
   //------------- Class Specific Request -------------//
@@ -447,53 +449,64 @@ tusb_error_t hidd_control_request_st(uint8_t rhport, tusb_control_request_t cons
         xferlen = p_request->wLength;
       }
 
-      STASK_ASSERT( xferlen > 0 );
-      usbd_control_xfer_st(rhport, p_request->bmRequestType_bit.direction, p_hid->report_buf, xferlen);
+      TU_ASSERT( xferlen > 0 );
+      dcd_edpt_xfer(rhport, TUSB_DIR_IN_MASK, _shared_control_buffer, xferlen);
     }
     else if ( HID_REQ_CONTROL_SET_REPORT == p_request->bRequest )
     {
-      usbd_control_xfer_st(rhport, p_request->bmRequestType_bit.direction, _usbd_ctrl_buf, p_request->wLength);
+      dcd_edpt_xfer(rhport, 0, _shared_control_buffer, p_request->wLength);
+    }
+    else if (HID_REQ_CONTROL_SET_IDLE == p_request->bRequest)
+    {
+      // TODO idle rate of report
+      p_hid->idle_rate = tu_u16_high(p_request->wValue);
+    }
+    else if (HID_REQ_CONTROL_GET_IDLE == p_request->bRequest)
+    {
+      // TODO idle rate of report
+      _shared_control_buffer[0] = p_hid->idle_rate;
+      dcd_edpt_xfer(rhport, TUSB_DIR_IN_MASK, _shared_control_buffer, 1);
+    }
+    else if (HID_REQ_CONTROL_GET_PROTOCOL == p_request->bRequest )
+    {
+      _shared_control_buffer[0] = 1-p_hid->boot_protocol;   // 0 is Boot, 1 is Report protocol
+      dcd_edpt_xfer(rhport, TUSB_DIR_IN_MASK, _shared_control_buffer, 1);
+    }
+    else if (HID_REQ_CONTROL_SET_PROTOCOL == p_request->bRequest )
+    {
+      p_hid->boot_protocol = 1 - p_request->wValue; // 0 is Boot, 1 is Report protocol
+    }else
+    {
+      return TUSB_ERROR_FAILED;
+    }
+  }else
+  {
+    return TUSB_ERROR_FAILED;
+  }
+  return TUSB_ERROR_NONE;
+}
 
+void hidd_control_request_complete(uint8_t rhport, tusb_control_request_t const * p_request)
+{
+  hidd_interface_t* p_hid = get_interface_by_itfnum( (uint8_t) p_request->wIndex );
+  if (p_hid == NULL) {
+      return;
+  }
+
+  if (p_request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS)
+  {
+    if ( HID_REQ_CONTROL_SET_REPORT == p_request->bRequest )
+    {
       // wValue = Report Type | Report ID
       uint8_t const report_type = tu_u16_high(p_request->wValue);
       uint8_t const report_id   = tu_u16_low(p_request->wValue);
 
       if ( p_hid->set_report_cb )
       {
-        p_hid->set_report_cb(report_id, (hid_report_type_t) report_type, _usbd_ctrl_buf, p_request->wLength);
+        p_hid->set_report_cb(report_id, (hid_report_type_t) report_type, _shared_control_buffer, p_request->wLength);
       }
     }
-    else if (HID_REQ_CONTROL_SET_IDLE == p_request->bRequest)
-    {
-      // TODO idle rate of report
-      p_hid->idle_rate = tu_u16_high(p_request->wValue);
-      dcd_control_status(rhport, p_request->bmRequestType_bit.direction);
-    }
-    else if (HID_REQ_CONTROL_GET_IDLE == p_request->bRequest)
-    {
-      // TODO idle rate of report
-      _usbd_ctrl_buf[0] = p_hid->idle_rate;
-      usbd_control_xfer_st(rhport, p_request->bmRequestType_bit.direction, _usbd_ctrl_buf, 1);
-    }
-    else if (HID_REQ_CONTROL_GET_PROTOCOL == p_request->bRequest )
-    {
-      _usbd_ctrl_buf[0] = 1-p_hid->boot_protocol;   // 0 is Boot, 1 is Report protocol
-      usbd_control_xfer_st(rhport, p_request->bmRequestType_bit.direction, _usbd_ctrl_buf, 1);
-    }
-    else if (HID_REQ_CONTROL_SET_PROTOCOL == p_request->bRequest )
-    {
-      p_hid->boot_protocol = 1 - p_request->wValue; // 0 is Boot, 1 is Report protocol
-      dcd_control_status(rhport, p_request->bmRequestType_bit.direction);
-    }else
-    {
-      dcd_control_stall(rhport);
-    }
-  }else
-  {
-    dcd_control_stall(rhport);
   }
-
-  OSAL_SUBTASK_END
 }
 
 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/hid/hid_device.h b/src/class/hid/hid_device.h
index d5b9f3831..1dc40af57 100644
--- a/src/class/hid/hid_device.h
+++ b/src/class/hid/hid_device.h
@@ -378,7 +378,8 @@ ATTR_WEAK void tud_hid_mouse_set_report_cb(uint8_t report_id, hid_report_type_t
 
 void hidd_init(void);
 tusb_error_t hidd_open(uint8_t rhport, tusb_desc_interface_t const * p_interface_desc, uint16_t *p_length);
-tusb_error_t hidd_control_request_st(uint8_t rhport, tusb_control_request_t const * p_request);
+tusb_error_t hidd_control_request(uint8_t rhport, tusb_control_request_t const * p_request, uint16_t bytes_already_sent);
+void hidd_control_request_complete (uint8_t rhport, tusb_control_request_t const * p_request);
 tusb_error_t hidd_xfer_cb(uint8_t rhport, uint8_t edpt_addr, tusb_event_t event, uint32_t xferred_bytes);
 void hidd_reset(uint8_t rhport);
 
@@ -389,5 +390,3 @@ void hidd_reset(uint8_t rhport);
 #endif
 
 #endif /* _TUSB_HID_DEVICE_H_ */
-
-
diff --git a/src/class/msc/msc.h b/src/class/msc/msc.h
index 59df6bed6..e7c32b303 100644
--- a/src/class/msc/msc.h
+++ b/src/class/msc/msc.h
@@ -52,6 +52,10 @@
  extern "C" {
 #endif
 
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wpacked"
+#pragma GCC diagnostic ignored "-Wattributes"
+
 //--------------------------------------------------------------------+
 // Mass Storage Class Constant
 //--------------------------------------------------------------------+
@@ -279,11 +283,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;
 
@@ -390,6 +396,8 @@ typedef struct ATTR_PACKED
 TU_VERIFY_STATIC(sizeof(scsi_read10_t) == 10, "size is not correct");
 TU_VERIFY_STATIC(sizeof(scsi_write10_t) == 10, "size is not correct");
 
+#pragma GCC diagnostic pop
+
 #ifdef __cplusplus
  }
 #endif
diff --git a/src/class/msc/msc_device.c b/src/class/msc/msc_device.c
index 5a047b016..32ad90102 100644
--- a/src/class/msc/msc_device.c
+++ b/src/class/msc/msc_device.c
@@ -47,6 +47,7 @@
 
 #include "common/tusb_common.h"
 #include "msc_device.h"
+#include "device/control.h"
 #include "device/usbd_pvt.h"
 
 //--------------------------------------------------------------------+
@@ -59,31 +60,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];
 
 //--------------------------------------------------------------------+
@@ -170,29 +147,41 @@ tusb_error_t mscd_open(uint8_t rhport, tusb_desc_interface_t const * p_desc_itf,
   return TUSB_ERROR_NONE;
 }
 
-tusb_error_t mscd_control_request_st(uint8_t rhport, tusb_control_request_t const * p_request)
+tusb_error_t mscd_control_request(uint8_t rhport, tusb_control_request_t const * p_request, uint16_t bytes_already_sent)
 {
-  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)
   {
-    dcd_control_status(rhport, p_request->bmRequestType_bit.direction);
+    // TODO: Actually reset.
   }
   else if (MSC_REQ_GET_MAX_LUN == p_request->bRequest)
   {
     // returned MAX LUN is minus 1 by specs
-    _usbd_ctrl_buf[0] = CFG_TUD_MSC_MAXLUN-1;
-    usbd_control_xfer_st(rhport, p_request->bmRequestType_bit.direction, _usbd_ctrl_buf, 1);
+    _shared_control_buffer[0] = CFG_TUD_MSC_MAXLUN-1;
+    dcd_edpt_xfer(rhport, TUSB_DIR_IN_MASK, _shared_control_buffer, 1);
   }else
   {
-    dcd_control_stall(rhport); // stall unsupported request
+    return TUSB_ERROR_FAILED; // stall unsupported request
   }
-
-  OSAL_SUBTASK_END
+  return TUSB_ERROR_NONE;
 }
 
+void mscd_control_request_complete(uint8_t rhport, tusb_control_request_t const * p_request)
+{
+  return;
+}
+
+// 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 +191,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 +209,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 +248,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 +296,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 +430,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..ac3eade22 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);
+
 /** @} */
 /** @} */
 
@@ -174,7 +198,8 @@ ATTR_WEAK void tud_msc_scsi_complete_cb(uint8_t lun, uint8_t const scsi_cmd[16])
 
 void mscd_init(void);
 tusb_error_t mscd_open(uint8_t rhport, tusb_desc_interface_t const * p_interface_desc, uint16_t *p_length);
-tusb_error_t mscd_control_request_st(uint8_t rhport, tusb_control_request_t const * p_request);
+tusb_error_t mscd_control_request(uint8_t rhport, tusb_control_request_t const * p_request, uint16_t bytes_already_sent);
+void mscd_control_request_complete (uint8_t rhport, tusb_control_request_t const * p_request);
 tusb_error_t mscd_xfer_cb(uint8_t rhport, uint8_t edpt_addr, tusb_event_t event, uint32_t xferred_bytes);
 void mscd_reset(uint8_t rhport);
 
@@ -185,4 +210,3 @@ void mscd_reset(uint8_t rhport);
 #endif
 
 #endif /* _TUSB_MSC_DEVICE_H_ */
-
diff --git a/src/common/tusb_types.h b/src/common/tusb_types.h
index 8ce334e55..5c226f556 100644
--- a/src/common/tusb_types.h
+++ b/src/common/tusb_types.h
@@ -51,6 +51,10 @@
  extern "C" {
 #endif
 
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wpacked"
+#pragma GCC diagnostic ignored "-Wattributes"
+
 /*------------------------------------------------------------------*/
 /* CONSTANTS
  *------------------------------------------------------------------*/
@@ -422,6 +426,8 @@ static inline uint8_t descriptor_len(uint8_t const p_desc[])
 // Convert comma-separated string to descriptor unicode format
 #define TUD_DESC_STRCONV( ... )     (const uint16_t[]) { TUD_DESC_STR_HEADER(VA_ARGS_NUM_(__VA_ARGS__)), __VA_ARGS__ }
 
+#pragma GCC diagnostic pop
+
 #ifdef __cplusplus
  }
 #endif
diff --git a/src/device/control.c b/src/device/control.c
new file mode 100644
index 000000000..5856f0628
--- /dev/null
+++ b/src/device/control.c
@@ -0,0 +1,262 @@
+/**************************************************************************/
+/*!
+    @file     usbd.c
+    @author   hathach (tinyusb.org)
+
+    @section LICENSE
+
+    Software License Agreement (BSD License)
+
+    Copyright (c) 2013, 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
+
+#define _TINY_USB_SOURCE_FILE_
+
+#include "tusb.h"
+#include "control.h"
+#include "device/usbd_pvt.h"
+
+control_t control_state;
+
+void controld_reset(uint8_t rhport) {
+    control_state.current_stage = CONTROL_STAGE_SETUP;
+}
+
+void controld_init(void) {
+}
+
+// Helper to send STATUS (zero length) packet
+// Note dir is value of direction bit in setup packet (i.e DATA stage direction)
+static inline bool dcd_control_status(uint8_t rhport, uint8_t dir)
+{
+  uint8_t ep_addr = 0;
+  // Invert the direction.
+  if (dir == TUSB_DIR_OUT) {
+    ep_addr |= TUSB_DIR_IN_MASK;
+  }
+  // status direction is reversed to one in the setup packet
+  return dcd_edpt_xfer(rhport, ep_addr, NULL, 0);
+}
+
+static inline void dcd_control_stall(uint8_t rhport)
+{
+  dcd_edpt_stall(rhport, 0 | TUSB_DIR_IN_MASK);
+}
+
+
+// return len of descriptor and change pointer to descriptor's buffer
+static uint16_t get_descriptor(uint8_t rhport, tusb_control_request_t const * const p_request, uint8_t const ** pp_buffer)
+{
+  (void) rhport;
+
+  tusb_desc_type_t const desc_type = (tusb_desc_type_t) tu_u16_high(p_request->wValue);
+  uint8_t const desc_index = tu_u16_low( p_request->wValue );
+
+  uint8_t const * desc_data = NULL ;
+  uint16_t len = 0;
+
+  switch(desc_type)
+  {
+    case TUSB_DESC_DEVICE:
+      desc_data = (uint8_t const *) usbd_desc_set->device;
+      len       = sizeof(tusb_desc_device_t);
+    break;
+
+    case TUSB_DESC_CONFIGURATION:
+      desc_data = (uint8_t const *) usbd_desc_set->config;
+      len       = ((tusb_desc_configuration_t const*) desc_data)->wTotalLength;
+    break;
+
+    case TUSB_DESC_STRING:
+      // String Descriptor always uses the desc set from user
+      if ( desc_index < tud_desc_set.string_count )
+      {
+        desc_data = tud_desc_set.string_arr[desc_index];
+        TU_VERIFY( desc_data != NULL, 0 );
+
+        len  = desc_data[0];  // first byte of descriptor is its size
+      }else
+      {
+        // out of range
+        /* The 0xee string is indeed a Microsoft USB extension.
+         * It can be used to tell Windows what driver it should use for the device !!!
+         */
+        return 0;
+      }
+    break;
+
+    case TUSB_DESC_DEVICE_QUALIFIER:
+      // TODO If not highspeed capable stall this request otherwise
+      // return the descriptor that could work in highspeed
+      return 0;
+    break;
+
+    default: return 0;
+  }
+
+  TU_ASSERT( desc_data != NULL, 0);
+
+  // up to Host's length
+  len = tu_min16(p_request->wLength, len );
+  (*pp_buffer) = desc_data;
+
+  return len;
+}
+
+tusb_error_t controld_xfer_cb(uint8_t rhport, uint8_t edpt_addr, tusb_event_t event, uint32_t xferred_bytes) {
+    if (control_state.current_stage == CONTROL_STAGE_STATUS && xferred_bytes == 0) {
+        control_state.current_stage = CONTROL_STAGE_SETUP;
+        return TUSB_ERROR_NONE;
+    }
+    tusb_error_t error = TUSB_ERROR_NONE;
+    control_state.total_transferred += xferred_bytes;
+    tusb_control_request_t const *p_request = &control_state.current_request;
+
+    if (p_request->wLength == control_state.total_transferred || xferred_bytes < 64) {
+        control_state.current_stage = CONTROL_STAGE_STATUS;
+        dcd_control_status(rhport, p_request->bmRequestType_bit.direction);
+
+        // Do the user callback after queueing the STATUS packet because the callback could be slow.
+        if ( TUSB_REQ_RCPT_INTERFACE == p_request->bmRequestType_bit.recipient )
+        {
+          tud_control_interface_control_complete_cb(rhport, tu_u16_low(p_request->wIndex), p_request);
+        }
+    } else {
+        if (TUSB_REQ_RCPT_INTERFACE == p_request->bmRequestType_bit.recipient) {
+          error = tud_control_interface_control_cb(rhport, tu_u16_low(p_request->wIndex), p_request, control_state.total_transferred);
+        } else {
+          error = controld_process_control_request(rhport, p_request, control_state.total_transferred);
+        }
+    }
+    return error;
+}
+
+// This tracks the state of a control request.
+tusb_error_t controld_process_setup_request(uint8_t rhport, tusb_control_request_t const * p_request) {
+  tusb_error_t error = TUSB_ERROR_NONE;
+  memcpy(&control_state.current_request, p_request, sizeof(tusb_control_request_t));
+  if (p_request->wLength == 0) {
+      control_state.current_stage = CONTROL_STAGE_STATUS;
+  } else {
+      control_state.current_stage = CONTROL_STAGE_DATA;
+      control_state.total_transferred = 0;
+  }
+
+  if ( TUSB_REQ_RCPT_INTERFACE == p_request->bmRequestType_bit.recipient )
+  {
+    error = tud_control_interface_control_cb(rhport, tu_u16_low(p_request->wIndex), p_request, 0);
+  } else {
+    error = controld_process_control_request(rhport, p_request, 0);
+  }
+
+  if (error != TUSB_ERROR_NONE) {
+    dcd_control_stall(rhport); // Stall errored requests
+  } else if (control_state.current_stage == CONTROL_STAGE_STATUS) {
+    dcd_control_status(rhport, p_request->bmRequestType_bit.direction);
+  }
+  return error;
+}
+
+// This handles the actual request and its response.
+tusb_error_t controld_process_control_request(uint8_t rhport, tusb_control_request_t const * p_request, uint16_t bytes_already_sent)
+{
+  tusb_error_t error = TUSB_ERROR_NONE;
+  uint8_t ep_addr = 0;
+  if (p_request->bmRequestType_bit.direction == TUSB_DIR_IN) {
+      ep_addr |= TUSB_DIR_IN_MASK;
+  }
+
+  //------------- Standard Request e.g in enumeration -------------//
+  if( TUSB_REQ_RCPT_DEVICE    == p_request->bmRequestType_bit.recipient &&
+      TUSB_REQ_TYPE_STANDARD  == p_request->bmRequestType_bit.type ) {
+    switch (p_request->bRequest) {
+      case TUSB_REQ_GET_DESCRIPTOR: {
+        uint8_t  const * buffer = NULL;
+        uint16_t const   len    = get_descriptor(rhport, p_request, &buffer);
+
+        if (len) {
+          uint16_t remaining_bytes = len - bytes_already_sent;
+          if (remaining_bytes > 64) {
+              remaining_bytes = 64;
+          }
+          memcpy(_shared_control_buffer, buffer + bytes_already_sent, remaining_bytes);
+          dcd_edpt_xfer(rhport, ep_addr, _shared_control_buffer, remaining_bytes);
+        } else {
+          return TUSB_ERROR_FAILED;
+        }
+        break;
+      }
+      case TUSB_REQ_GET_CONFIGURATION:
+        memcpy(_shared_control_buffer, &control_state.config, 1);
+        dcd_edpt_xfer(rhport, ep_addr, _shared_control_buffer, 1);
+        break;
+      case TUSB_REQ_SET_ADDRESS:
+        dcd_set_address(rhport, (uint8_t) p_request->wValue);
+        break;
+      case TUSB_REQ_SET_CONFIGURATION:
+        control_state.config = p_request->wValue;
+        tud_control_set_config_cb (rhport, control_state.config);
+        break;
+      default:
+        return TUSB_ERROR_FAILED;
+    }
+  } else if (p_request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_ENDPOINT &&
+             p_request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD) {
+    //------------- Endpoint Request -------------//
+    switch (p_request->bRequest) {
+      case TUSB_REQ_GET_STATUS: {
+        uint16_t status = dcd_edpt_stalled(rhport, tu_u16_low(p_request->wIndex)) ? 0x0001 : 0x0000;
+        memcpy(_shared_control_buffer, &status, 2);
+
+        dcd_edpt_xfer(rhport, ep_addr, _shared_control_buffer, 2);
+        break;
+      }
+      case TUSB_REQ_CLEAR_FEATURE:
+        // only endpoint feature is halted/stalled
+        dcd_edpt_clear_stall(rhport, tu_u16_low(p_request->wIndex));
+        break;
+      case TUSB_REQ_SET_FEATURE:
+        // only endpoint feature is halted/stalled
+        dcd_edpt_stall(rhport, tu_u16_low(p_request->wIndex));
+        break;
+      default:
+        return TUSB_ERROR_FAILED;
+    }
+  } else {
+    //------------- Unsupported Request -------------//
+    return TUSB_ERROR_FAILED;
+  }
+  return error;
+}
+
+#endif
diff --git a/src/device/control.h b/src/device/control.h
new file mode 100644
index 000000000..881c99ead
--- /dev/null
+++ b/src/device/control.h
@@ -0,0 +1,98 @@
+/**************************************************************************/
+/*!
+    @file     usbd.h
+    @author   hathach (tinyusb.org)
+
+    @section LICENSE
+
+    Software License Agreement (BSD License)
+
+    Copyright (c) 2013, 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.
+*/
+/**************************************************************************/
+
+/** \ingroup group_usbd
+ *  @{ */
+
+#ifndef _TUSB_CONTROL_H_
+#define _TUSB_CONTROL_H_
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+#include "tusb.h"
+
+typedef enum {
+    CONTROL_STAGE_SETUP, // Waiting for a setup token.
+    CONTROL_STAGE_DATA, // In the process of sending or receiving data.
+    CONTROL_STAGE_STATUS // In the process of transmitting the STATUS ZLP.
+} control_stage_t;
+
+typedef struct {
+    control_stage_t current_stage;
+    tusb_control_request_t current_request;
+    uint16_t total_transferred;
+    uint8_t config;
+} control_t;
+
+CFG_TUSB_ATTR_USBRAM CFG_TUSB_MEM_ALIGN uint8_t _shared_control_buffer[64];
+
+tusb_error_t controld_process_setup_request(uint8_t rhport, tusb_control_request_t const * const p_request);
+
+// Callback when the configuration of the device is changed.
+tusb_error_t tud_control_set_config_cb(uint8_t rhport, uint8_t config_number);
+
+// Called when the DATA stage of a control transaction is complete.
+void tud_control_interface_control_complete_cb(uint8_t rhport, uint8_t interface, tusb_control_request_t const * const p_request);
+
+tusb_error_t tud_control_interface_control_cb(uint8_t rhport, uint8_t interface, tusb_control_request_t const * const p_request, uint16_t bytes_already_sent);
+
+//--------------------------------------------------------------------+
+// INTERNAL API
+//--------------------------------------------------------------------+
+
+void controld_init(void);
+tusb_error_t controld_open(uint8_t rhport, tusb_desc_interface_t const * p_interface_desc, uint16_t *p_length);
+
+// This tracks the state of a control request.
+tusb_error_t controld_process_setup_request(uint8_t rhport, tusb_control_request_t const * p_request);
+
+// This handles the actual request and its response.
+tusb_error_t controld_process_control_request(uint8_t rhport, tusb_control_request_t const * p_request, uint16_t bytes_already_sent);
+
+tusb_error_t controld_xfer_cb(uint8_t rhport, uint8_t edpt_addr, tusb_event_t event, uint32_t xferred_bytes);
+void controld_reset(uint8_t rhport);
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif /* _TUSB_CONTROL_H_ */
+
+/** @} */
diff --git a/src/device/dcd.h b/src/device/dcd.h
index 87837df20..0c976edec 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);
 
 
 /*------------------------------------------------------------------*/
@@ -154,22 +135,6 @@ void dcd_edpt_stall       (uint8_t rhport, uint8_t ep_addr);
 void dcd_edpt_clear_stall (uint8_t rhport, uint8_t ep_addr);
 bool dcd_edpt_stalled     (uint8_t rhport, uint8_t ep_addr);
 
-//------------- Control Endpoint -------------//
-bool dcd_control_xfer     (uint8_t rhport, uint8_t dir, uint8_t * buffer, uint16_t length);
-
-// Helper to send STATUS (zero length) packet
-// Note dir is value of direction bit in setup packet (i.e DATA stage direction)
-static inline bool dcd_control_status(uint8_t rhport, uint8_t dir)
-{
-  // status direction is reversed to one in the setup packet
-  return dcd_control_xfer(rhport, 1-dir, NULL, 0);
-}
-
-static inline void dcd_control_stall(uint8_t rhport)
-{
-  dcd_edpt_stall(rhport, 0);
-}
-
 #ifdef __cplusplus
  }
 #endif
diff --git a/src/device/usbd.c b/src/device/usbd.c
index 1c2fa5d5d..c9d5e5642 100644
--- a/src/device/usbd.c
+++ b/src/device/usbd.c
@@ -36,12 +36,15 @@
 */
 /**************************************************************************/
 
+// This top level class manages the bus state and delegates events to class-specific drivers.
+
 #include "tusb_option.h"
 
 #if TUSB_OPT_DEVICE_ENABLED
 
 #define _TINY_USB_SOURCE_FILE_
 
+#include "control.h"
 #include "tusb.h"
 #include "usbd.h"
 #include "device/usbd_pvt.h"
@@ -72,7 +75,6 @@ typedef struct {
   uint8_t ep2drv[2][8];
 }usbd_device_t;
 
-CFG_TUSB_ATTR_USBRAM CFG_TUSB_MEM_ALIGN uint8_t _usbd_ctrl_buf[CFG_TUD_CTRL_BUFSIZE];
 static usbd_device_t _usbd_dev;
 
 
@@ -92,7 +94,9 @@ typedef struct {
 
   void         (* init           ) (void);
   tusb_error_t (* open           ) (uint8_t rhport, tusb_desc_interface_t const * desc_intf, uint16_t* p_length);
-  tusb_error_t (* control_req_st ) (uint8_t rhport, tusb_control_request_t const *);
+  // Control request is called one or more times for a request and can queue multiple data packets.
+  tusb_error_t (* control_request ) (uint8_t rhport, tusb_control_request_t const *, uint16_t bytes_already_sent);
+  void (* control_request_complete ) (uint8_t rhport, tusb_control_request_t const *);
   tusb_error_t (* xfer_cb        ) (uint8_t rhport, uint8_t ep_addr, tusb_event_t, uint32_t);
   void         (* sof            ) (uint8_t rhport);
   void         (* reset          ) (uint8_t);
@@ -100,52 +104,66 @@ typedef struct {
 
 static usbd_class_driver_t const usbd_class_drivers[] =
 {
+    {
+        .class_code      = TUSB_CLASS_UNSPECIFIED,
+        .init            = controld_init,
+        .open            = NULL,
+        .control_request = NULL,
+        .control_request_complete = NULL,
+        .xfer_cb         = controld_xfer_cb,
+        .sof             = NULL,
+        .reset           = controld_reset
+    },
   #if CFG_TUD_CDC
     {
-        .class_code     = TUSB_CLASS_CDC,
-        .init           = cdcd_init,
-        .open           = cdcd_open,
-        .control_req_st = cdcd_control_request_st,
-        .xfer_cb        = cdcd_xfer_cb,
-        .sof            = NULL,
-        .reset          = cdcd_reset
+        .class_code      = TUSB_CLASS_CDC,
+        .init            = cdcd_init,
+        .open            = cdcd_open,
+        .control_request = cdcd_control_request,
+        .control_request_complete = cdcd_control_request_complete,
+        .xfer_cb         = cdcd_xfer_cb,
+        .sof             = NULL,
+        .reset           = cdcd_reset
     },
   #endif
 
   #if CFG_TUD_MSC
     {
-        .class_code     = TUSB_CLASS_MSC,
-        .init           = mscd_init,
-        .open           = mscd_open,
-        .control_req_st = mscd_control_request_st,
-        .xfer_cb        = mscd_xfer_cb,
-        .sof            = NULL,
-        .reset          = mscd_reset
+        .class_code      = TUSB_CLASS_MSC,
+        .init            = mscd_init,
+        .open            = mscd_open,
+        .control_request = mscd_control_request,
+        .control_request_complete = mscd_control_request_complete,
+        .xfer_cb         = mscd_xfer_cb,
+        .sof             = NULL,
+        .reset           = mscd_reset
     },
   #endif
 
 
   #if CFG_TUD_HID
     {
-        .class_code     = TUSB_CLASS_HID,
-        .init           = hidd_init,
-        .open           = hidd_open,
-        .control_req_st = hidd_control_request_st,
-        .xfer_cb        = hidd_xfer_cb,
-        .sof            = NULL,
-        .reset          = hidd_reset
+        .class_code      = TUSB_CLASS_HID,
+        .init            = hidd_init,
+        .open            = hidd_open,
+        .control_request = hidd_control_request,
+        .control_request_complete = hidd_control_request_complete,
+        .xfer_cb         = hidd_xfer_cb,
+        .sof             = NULL,
+        .reset           = hidd_reset
     },
   #endif
 
   #if CFG_TUD_CUSTOM_CLASS
     {
-        .class_code     = TUSB_CLASS_VENDOR_SPECIFIC,
-        .init           = cusd_init,
-        .open           = cusd_open,
-        .control_req_st = cusd_control_request_st,
-        .xfer_cb        = cusd_xfer_cb,
-        .sof            = NULL,
-        .reset          = cusd_reset
+        .class_code      = TUSB_CLASS_VENDOR_SPECIFIC,
+        .init            = cusd_init,
+        .open            = cusd_open,
+        .control_request = cusd_control_request,
+        .control_request_complete = cusd_control_request_complete,
+        .xfer_cb         = cusd_xfer_cb,
+        .sof             = NULL,
+        .reset           = cusd_reset
     },
   #endif
 };
@@ -162,16 +180,10 @@ OSAL_TASK_DEF(_usbd_task_def, "usbd", usbd_task, CFG_TUD_TASK_PRIO, CFG_TUD_TASK
 OSAL_QUEUE_DEF(_usbd_qdef, CFG_TUD_TASK_QUEUE_SZ, dcd_event_t);
 static osal_queue_t _usbd_q;
 
-/*------------- control transfer semaphore -------------*/
-static osal_semaphore_def_t _usbd_sem_def;
-osal_semaphore_t _usbd_ctrl_sem;
-
 //--------------------------------------------------------------------+
 // INTERNAL FUNCTION
 //--------------------------------------------------------------------+
 static void mark_interface_endpoint(uint8_t const* p_desc, uint16_t desc_len, uint8_t driver_id);
-static tusb_error_t proc_set_config_req(uint8_t rhport, uint8_t config_number);
-static uint16_t get_descriptor(uint8_t rhport, tusb_control_request_t const * const p_request, uint8_t const ** pp_buffer);
 
 //--------------------------------------------------------------------+
 // APPLICATION API
@@ -184,7 +196,6 @@ bool tud_mounted(void)
 //--------------------------------------------------------------------+
 // IMPLEMENTATION
 //--------------------------------------------------------------------+
-static tusb_error_t proc_control_request_st(uint8_t rhport, tusb_control_request_t const * const p_request);
 static tusb_error_t usbd_main_st(void);
 
 tusb_error_t usbd_init (void)
@@ -201,9 +212,6 @@ tusb_error_t usbd_init (void)
   _usbd_q = osal_queue_create(&_usbd_qdef);
   TU_VERIFY(_usbd_q, TUSB_ERROR_OSAL_QUEUE_FAILED);
 
-  _usbd_ctrl_sem = osal_semaphore_create(&_usbd_sem_def);
-  TU_VERIFY(_usbd_q, TUSB_ERROR_OSAL_SEMAPHORE_FAILED);
-
   osal_task_create(&_usbd_task_def);
 
   //------------- class init -------------//
@@ -217,6 +225,9 @@ static void usbd_reset(uint8_t rhport)
   tu_varclr(&_usbd_dev);
   memset(_usbd_dev.itf2drv, 0xff, sizeof(_usbd_dev.itf2drv)); // invalid mapping
   memset(_usbd_dev.ep2drv , 0xff, sizeof(_usbd_dev.ep2drv )); // invalid mapping
+  // Always map the 0th endpoint to the control driver.
+  _usbd_dev.ep2drv[TUSB_DIR_IN][0] = 0;
+  _usbd_dev.ep2drv[TUSB_DIR_OUT][0] = 0;
 
   for (uint8_t i = 0; i < USBD_CLASS_DRIVER_COUNT; i++)
   {
@@ -241,23 +252,22 @@ void usbd_task( void* param)
 
 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 );
+      // Setup tokens are unique to the Control endpointso we delegate to it directly.
+      controld_process_setup_request(event.rhport, &event.setup_received);
     }
     else if (DCD_EVENT_XFER_COMPLETE == event.event_id)
     {
@@ -267,20 +277,18 @@ 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)
     {
       usbd_reset(event.rhport);
       osal_queue_reset(_usbd_q);
-      osal_semaphore_reset(_usbd_ctrl_sem);
     }
     else if (DCD_EVENT_UNPLUGGED == event.event_id)
     {
       usbd_reset(event.rhport);
       osal_queue_reset(_usbd_q);
-      osal_semaphore_reset(_usbd_ctrl_sem);
 
       tud_umount_cb(); // invoke callback
     }
@@ -304,116 +312,31 @@ static tusb_error_t usbd_main_st(void)
     }
   }
 
-  OSAL_SUBTASK_END
+    return err;
 }
 
-//--------------------------------------------------------------------+
-// CONTROL REQUEST
-//--------------------------------------------------------------------+
-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;
-
-  //------------- Standard Request e.g in enumeration -------------//
-  if( TUSB_REQ_RCPT_DEVICE    == p_request->bmRequestType_bit.recipient &&
-      TUSB_REQ_TYPE_STANDARD  == p_request->bmRequestType_bit.type )
-  {
-    if ( TUSB_REQ_GET_DESCRIPTOR == p_request->bRequest )
+void tud_control_interface_control_complete_cb(uint8_t rhport, uint8_t interface, tusb_control_request_t const * const p_request) {
+    if (_usbd_dev.itf2drv[ interface ] < USBD_CLASS_DRIVER_COUNT)
     {
-      uint8_t  const * buffer = NULL;
-      uint16_t const   len    = get_descriptor(rhport, p_request, &buffer);
-
-      if ( len )
-      {
-        STASK_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
-      {
-        dcd_control_stall(rhport); // stall unsupported descriptor
+      const usbd_class_driver_t *driver = &usbd_class_drivers[_usbd_dev.itf2drv[interface]];
+      if (driver->control_request_complete != NULL) {
+        driver->control_request_complete(rhport, p_request);
       }
     }
-    else if (TUSB_REQ_GET_CONFIGURATION == p_request->bRequest )
-    {
-      memcpy(_usbd_ctrl_buf, &_usbd_dev.config_num, 1);
-      usbd_control_xfer_st(rhport, p_request->bmRequestType_bit.direction, _usbd_ctrl_buf, 1);
-    }
-    else if ( TUSB_REQ_SET_ADDRESS == p_request->bRequest )
-    {
-      dcd_set_address(rhport, (uint8_t) p_request->wValue);
+}
 
-      #if CFG_TUSB_MCU != OPT_MCU_NRF5X // nrf5x auto handle set address, we must not return status
-      dcd_control_status(rhport, p_request->bmRequestType_bit.direction);
-      #endif
-    }
-    else if ( TUSB_REQ_SET_CONFIGURATION == p_request->bRequest )
+tusb_error_t tud_control_interface_control_cb(uint8_t rhport, uint8_t interface, tusb_control_request_t const * const p_request, uint16_t bytes_already_sent) {
+    if (_usbd_dev.itf2drv[ interface ] < USBD_CLASS_DRIVER_COUNT)
     {
-      proc_set_config_req(rhport, (uint8_t) p_request->wValue);
-      dcd_control_status(rhport, p_request->bmRequestType_bit.direction);
+      return usbd_class_drivers[_usbd_dev.itf2drv[interface]].control_request(rhport, p_request, bytes_already_sent);
     }
-    else
-    {
-      dcd_control_stall(rhport); // Stall unsupported request
-    }
-  }
-
-  //------------- Class/Interface Specific Request -------------//
-  else if ( TUSB_REQ_RCPT_INTERFACE == p_request->bmRequestType_bit.recipient )
-  {
-    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 );
-    }else
-    {
-      dcd_control_stall(rhport); // Stall unsupported request
-    }
-  }
-
-  //------------- Endpoint Request -------------//
-  else if ( TUSB_REQ_RCPT_ENDPOINT == p_request->bmRequestType_bit.recipient &&
-            TUSB_REQ_TYPE_STANDARD == p_request->bmRequestType_bit.type)
-  {
-    if (TUSB_REQ_GET_STATUS == p_request->bRequest )
-    {
-      uint16_t status = dcd_edpt_stalled(rhport, tu_u16_low(p_request->wIndex)) ? 0x0001 : 0x0000;
-      memcpy(_usbd_ctrl_buf, &status, 2);
-
-      usbd_control_xfer_st(rhport, p_request->bmRequestType_bit.direction, _usbd_ctrl_buf, 2);
-    }
-    else if (TUSB_REQ_CLEAR_FEATURE == p_request->bRequest )
-    {
-      // only endpoint feature is halted/stalled
-      dcd_edpt_clear_stall(rhport, tu_u16_low(p_request->wIndex));
-      dcd_control_status(rhport, p_request->bmRequestType_bit.direction);
-    }
-    else if (TUSB_REQ_SET_FEATURE == p_request->bRequest )
-    {
-      // only endpoint feature is halted/stalled
-      dcd_edpt_stall(rhport, tu_u16_low(p_request->wIndex));
-      dcd_control_status(rhport, p_request->bmRequestType_bit.direction);
-    }
-    else
-    {
-      dcd_control_stall(rhport); // Stall unsupported request
-    }
-  }
-
-  //------------- Unsupported Request -------------//
-  else
-  {
-    dcd_control_stall(rhport); // Stall unsupported request
-  }
-
-  OSAL_SUBTASK_END
+    return TUSB_ERROR_FAILED;
 }
 
 // Process Set Configure Request
 // TODO Host (windows) can get HID report descriptor before set configured
 // may need to open interface before set configured
-static tusb_error_t proc_set_config_req(uint8_t rhport, uint8_t config_number)
+tusb_error_t tud_control_set_config_cb(uint8_t rhport, uint8_t config_number)
 {
   dcd_set_config(rhport, config_number);
 
@@ -465,65 +388,6 @@ static tusb_error_t proc_set_config_req(uint8_t rhport, uint8_t config_number)
   return TUSB_ERROR_NONE;
 }
 
-// return len of descriptor and change pointer to descriptor's buffer
-static uint16_t get_descriptor(uint8_t rhport, tusb_control_request_t const * const p_request, uint8_t const ** pp_buffer)
-{
-  (void) rhport;
-
-  tusb_desc_type_t const desc_type = (tusb_desc_type_t) tu_u16_high(p_request->wValue);
-  uint8_t const desc_index = tu_u16_low( p_request->wValue );
-
-  uint8_t const * desc_data = NULL ;
-  uint16_t len = 0;
-
-  switch(desc_type)
-  {
-    case TUSB_DESC_DEVICE:
-      desc_data = (uint8_t const *) usbd_desc_set->device;
-      len       = sizeof(tusb_desc_device_t);
-    break;
-
-    case TUSB_DESC_CONFIGURATION:
-      desc_data = (uint8_t const *) usbd_desc_set->config;
-      len       = ((tusb_desc_configuration_t const*) desc_data)->wTotalLength;
-    break;
-
-    case TUSB_DESC_STRING:
-      // String Descriptor always uses the desc set from user
-      if ( desc_index < tud_desc_set.string_count )
-      {
-        desc_data = tud_desc_set.string_arr[desc_index];
-        TU_VERIFY( desc_data != NULL, 0 );
-
-        len  = desc_data[0];  // first byte of descriptor is its size
-      }else
-      {
-        // out of range
-        /* The 0xee string is indeed a Microsoft USB extension.
-         * It can be used to tell Windows what driver it should use for the device !!!
-         */
-        return 0;
-      }
-    break;
-
-    case TUSB_DESC_DEVICE_QUALIFIER:
-      // TODO If not highspeed capable stall this request otherwise
-      // return the descriptor that could work in highspeed
-      return 0;
-    break;
-
-    default: return 0;
-  }
-
-  TU_ASSERT( desc_data != NULL, 0);
-
-  // up to Host's length
-  len = tu_min16(p_request->wLength, len );
-  (*pp_buffer) = desc_data;
-
-  return len;
-}
-
 // Helper marking endpoint of interface belongs to class driver
 static void mark_interface_endpoint(uint8_t const* p_desc, uint16_t desc_len, uint8_t driver_id)
 {
@@ -548,8 +412,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:
@@ -571,18 +433,7 @@ void dcd_event_handler(dcd_event_t const * event, bool in_isr)
     break;
 
     case DCD_EVENT_XFER_COMPLETE:
-      if (event->xfer_complete.ep_addr == 0)
-      {
-        // only signal data stage, skip status (zero byte)
-        if (event->xfer_complete.len)
-        {
-          (void) event->xfer_complete.result; // TODO handle control error/stalled
-          osal_semaphore_post( _usbd_ctrl_sem, in_isr);
-        }
-      }else
-      {
-        osal_queue_send(_usbd_q, event, in_isr);
-      }
+      osal_queue_send(_usbd_q, event, in_isr);
       TU_ASSERT(event->xfer_complete.result == DCD_XFER_SUCCESS,);
     break;
 
@@ -590,9 +441,40 @@ 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
 //--------------------------------------------------------------------+
+
 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..bbbe0a604 100644
--- a/src/device/usbd_pvt.h
+++ b/src/device/usbd_pvt.h
@@ -45,10 +45,6 @@
  extern "C" {
 #endif
 
-// for used by usbd_control_xfer_st() only, must not be used directly
-extern osal_semaphore_t _usbd_ctrl_sem;
-extern uint8_t _usbd_ctrl_buf[CFG_TUD_CTRL_BUFSIZE];
-
 // Either point to tud_desc_set or usbd_auto_desc_set depending on CFG_TUD_DESC_AUTO
 extern tud_desc_set_t const* usbd_desc_set;
 
@@ -64,21 +60,6 @@ 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)
-
-
 /*------------------------------------------------------------------*/
 /* 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 f70c99c7f..383809180 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
@@ -205,22 +157,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..33cbc8d92
--- /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_SAMD21
+
+#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..74b7bb7b8 100644
--- a/src/portable/nordic/nrf5x/dcd_nrf5x.c
+++ b/src/portable/nordic/nrf5x/dcd_nrf5x.c
@@ -82,17 +82,8 @@ typedef struct
 
 /*static*/ struct
 {
-  struct
-  {
-    uint8_t* buffer;
-    uint16_t total_len;
-    volatile uint16_t actual_len;
-
-    uint8_t  dir;
-  }control;
-
-  // Non control: 7 endpoints IN & OUT (offset 1)
-  nom_xfer_t xfer[7][2];
+  // All 8 endpoints including control IN & OUT (offset 1)
+  nom_xfer_t xfer[8][2];
 
   volatile bool dma_running;
 }_dcd;
@@ -109,6 +100,8 @@ void bus_reset(void)
   NRF_USBD->TASKS_STARTISOOUT = 0;
 
   tu_varclr(&_dcd);
+  _dcd.xfer[0][TUSB_DIR_IN].mps = MAX_PACKET_SIZE;
+  _dcd.xfer[0][TUSB_DIR_OUT].mps = MAX_PACKET_SIZE;
 }
 
 /*------------------------------------------------------------------*/
@@ -176,61 +169,13 @@ static void edpt_dma_end(void)
   _dcd.dma_running = false;
 }
 
-static void xact_control_start(void)
-{
-  // Each transaction is up to 64 bytes
-  uint8_t const xact_len = tu_min16(_dcd.control.total_len-_dcd.control.actual_len, MAX_PACKET_SIZE);
-
-  if ( _dcd.control.dir == TUSB_DIR_OUT )
-  {
-    // TODO control out
-    NRF_USBD->EPOUT[0].PTR    = (uint32_t) _dcd.control.buffer;
-    NRF_USBD->EPOUT[0].MAXCNT = xact_len;
-
-    NRF_USBD->TASKS_EP0RCVOUT = 1;
-    __ISB(); __DSB();
-  }else
-  {
-    NRF_USBD->EPIN[0].PTR        = (uint32_t) _dcd.control.buffer;
-    NRF_USBD->EPIN[0].MAXCNT     = xact_len;
-
-    edpt_dma_start(&NRF_USBD->TASKS_STARTEPIN[0]);
-  }
-
-  _dcd.control.buffer     += xact_len;
-  _dcd.control.actual_len += xact_len;
-}
-
-bool dcd_control_xfer (uint8_t rhport, uint8_t dir, uint8_t * buffer, uint16_t length)
-{
-  (void) rhport;
-
-  if ( length )
-  {
-    // Data Phase
-    _dcd.control.total_len  = length;
-    _dcd.control.actual_len = 0;
-    _dcd.control.buffer     = buffer;
-    _dcd.control.dir        = dir;
-
-    xact_control_start();
-  }else
-  {
-    // Status Phase also require Easy DMA has to be free as well !!!!
-    edpt_dma_start(&NRF_USBD->TASKS_EP0STATUS);
-    edpt_dma_end();
-  }
-
-  return true;
-}
-
 /*------------------------------------------------------------------*/
 /*
  *------------------------------------------------------------------*/
 
 static inline nom_xfer_t* get_td(uint8_t epnum, uint8_t dir)
 {
-  return &_dcd.xfer[epnum-1][dir];
+  return &_dcd.xfer[epnum][dir];
 }
 
 /*------------- Bulk/Int OUT transfer -------------*/
@@ -292,7 +237,7 @@ bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt)
   uint8_t const epnum = edpt_number(desc_edpt->bEndpointAddress);
   uint8_t const dir   = edpt_dir(desc_edpt->bEndpointAddress);
 
-  _dcd.xfer[epnum-1][dir].mps = desc_edpt->wMaxPacketSize.size;
+  _dcd.xfer[epnum][dir].mps = desc_edpt->wMaxPacketSize.size;
 
   if ( dir == TUSB_DIR_OUT )
   {
@@ -308,6 +253,16 @@ bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt)
   return true;
 }
 
+void control_status_token(uint8_t addr) {
+  NRF_USBD->EPIN[0].PTR        = 0;
+  NRF_USBD->EPIN[0].MAXCNT     = 0;
+  // Status Phase also require Easy DMA has to be free as well !!!!
+  NRF_USBD->TASKS_EP0STATUS = 1;
+
+  // The nRF doesn't interrupt on status transmit so we queue up a success response.
+  dcd_event_xfer_complete(0, addr, 0, DCD_XFER_SUCCESS, false);
+}
+
 bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes)
 {
   (void) rhport;
@@ -321,7 +276,10 @@ bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t
   xfer->total_len  = total_bytes;
   xfer->actual_len = 0;
 
-  if ( dir == TUSB_DIR_OUT )
+  // How does the control endpoint handle a ZLP in the data phase?
+  if (epnum == 0 && total_bytes == 0) {
+      control_status_token(ep_addr);
+  } else if ( dir == TUSB_DIR_OUT )
   {
     if ( xfer->data_received )
     {
@@ -427,47 +385,15 @@ void USBD_IRQHandler(void)
     edpt_dma_end();
   }
 
-  /*------------- Control Transfer -------------*/
+  // Setup tokens are specific to the Control endpoint.
   if ( int_status & USBD_INTEN_EP0SETUP_Msk )
   {
     uint8_t setup[8] = {
         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);
-  }
-
-  if ( int_status & USBD_INTEN_EP0DATADONE_Msk )
-  {
-    if ( _dcd.control.dir == TUSB_DIR_OUT )
-    {
-      // Control OUT: data from Host -> Endpoint
-      // Trigger DMA to move Endpoint -> SRAM
-      edpt_dma_start(&NRF_USBD->TASKS_STARTEPOUT[0]);
-    }else
-    {
-      // Control IN: data transferred from Endpoint -> Host
-      if ( _dcd.control.actual_len < _dcd.control.total_len )
-      {
-        xact_control_start();
-      }else
-      {
-        // Control IN complete
-        dcd_event_xfer_complete(0, 0, _dcd.control.actual_len, DCD_XFER_SUCCESS, true);
-      }
-    }
-  }
-
-  // Control OUT: data from Endpoint -> SRAM
-  if ( int_status & USBD_INTEN_ENDEPOUT0_Msk)
-  {
-    if ( _dcd.control.actual_len < _dcd.control.total_len )
-    {
-      xact_control_start();
-    }else
-    {
-      // Control OUT complete
-      dcd_event_xfer_complete(0, 0, _dcd.control.actual_len, DCD_XFER_SUCCESS, true);
+    if (setup[1] != TUSB_REQ_SET_ADDRESS) {
+      dcd_event_setup_received(0, setup, true);
     }
   }
 
@@ -478,9 +404,9 @@ void USBD_IRQHandler(void)
    * We must handle this stage before Host -> Endpoint just in case
    * 2 event happens at once
    */
-  for(uint8_t epnum=1; epnum<8; epnum++)
+  for(uint8_t epnum=0; epnum<8; epnum++)
   {
-    if ( BIT_TEST_(int_status, USBD_INTEN_ENDEPOUT0_Pos+epnum) )
+    if ( BIT_TEST_(int_status, USBD_INTEN_ENDEPOUT0_Pos+epnum))
     {
       nom_xfer_t* xfer = get_td(epnum, TUSB_DIR_OUT);
 
@@ -505,16 +431,16 @@ void USBD_IRQHandler(void)
     // Ended event for Bulk/Int : nothing to do
   }
 
-  if ( int_status & USBD_INTEN_EPDATA_Msk)
+  if ( int_status & USBD_INTEN_EPDATA_Msk || int_status & USBD_INTEN_EP0DATADONE_Msk)
   {
     uint32_t data_status = NRF_USBD->EPDATASTATUS;
 
     nrf_usbd_epdatastatus_clear(data_status);
 
     // Bulk/Int In: data from Endpoint -> Host
-    for(uint8_t epnum=1; epnum<8; epnum++)
+    for(uint8_t epnum=0; epnum<8; epnum++)
     {
-      if ( BIT_TEST_(data_status, epnum ) )
+      if ( BIT_TEST_(data_status, epnum ) || (epnum == 0 && BIT_TEST_(int_status, USBD_INTEN_EP0DATADONE_Pos)))
       {
         nom_xfer_t* xfer = get_td(epnum, TUSB_DIR_IN);
 
@@ -533,7 +459,7 @@ void USBD_IRQHandler(void)
     }
 
     // Bulk/Int OUT: data from Host -> Endpoint
-    for(uint8_t epnum=1; epnum<8; epnum++)
+    for(uint8_t epnum=0; epnum<8; epnum++)
     {
       if ( BIT_TEST_(data_status, 16+epnum ) )
       {
diff --git a/src/tusb_option.h b/src/tusb_option.h
index 846367129..d027ea0e6 100644
--- a/src/tusb_option.h
+++ b/src/tusb_option.h
@@ -57,6 +57,9 @@
 #define OPT_MCU_LPC43XX        7 ///< NXP LPC43xx series
 
 #define OPT_MCU_NRF5X        100 ///< Nordic nRF5x series
+
+#define OPT_MCU_SAMD21        200 ///< MicroChip SAMD21
+#define OPT_MCU_SAMD51        201 ///< MicroChip SAMD51
 /** @} */
 
 /** \defgroup group_supported_os Supported RTOS
@@ -147,6 +150,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