Просмотр исходного кода

Merge pull request #1058 from hathach/usbcv-compliant-test

nrf5x USB Compliance Verification Test suite
Ha Thach 4 лет назад
Родитель
Сommit
38f5aee9c3
47 измененных файлов с 725 добавлено и 238 удалено
  1. 1 1
      examples/device/audio_4_channel_mic/src/usb_descriptors.c
  2. 1 1
      examples/device/audio_test/src/usb_descriptors.c
  3. 2 2
      examples/device/cdc_dual_ports/src/usb_descriptors.c
  4. 18 1
      examples/device/cdc_msc/src/msc_disk.c
  5. 2 2
      examples/device/cdc_msc/src/usb_descriptors.c
  6. 7 1
      examples/device/cdc_msc_freertos/src/msc_disk.c
  7. 2 2
      examples/device/cdc_msc_freertos/src/usb_descriptors.c
  8. 1 1
      examples/device/dfu/src/usb_descriptors.c
  9. 1 1
      examples/device/dfu_runtime/src/usb_descriptors.c
  10. 7 1
      examples/device/dynamic_configuration/src/msc_disk.c
  11. 2 2
      examples/device/dynamic_configuration/src/usb_descriptors.c
  12. 1 1
      examples/device/hid_generic_inout/src/usb_descriptors.c
  13. 2 2
      examples/device/midi_test/src/usb_descriptors.c
  14. 18 1
      examples/device/msc_dual_lun/src/msc_disk_dual.c
  15. 2 2
      examples/device/msc_dual_lun/src/usb_descriptors.c
  16. 1 1
      examples/device/uac2_headset/src/usb_descriptors.c
  17. 1 1
      examples/device/usbtmc/src/usb_descriptors.c
  18. 1 1
      examples/device/webusb_serial/src/usb_descriptors.c
  19. 10 10
      src/class/hid/hid_device.h
  20. 380 166
      src/class/msc/msc_device.c
  21. 1 1
      src/class/msc/msc_device.h
  22. 6 1
      src/common/tusb_common.h
  23. 5 0
      src/device/dcd.h
  24. 56 25
      src/device/usbd.c
  25. 6 0
      src/portable/dialog/da146xx/dcd_da146xx.c
  26. 6 0
      src/portable/espressif/esp32sx/dcd_esp32sx.c
  27. 6 0
      src/portable/microchip/samd/dcd_samd.c
  28. 6 0
      src/portable/microchip/samg/dcd_samg.c
  29. 6 0
      src/portable/microchip/samx7x/dcd_samx7x.c
  30. 6 0
      src/portable/mindmotion/mm32/dcd_mm32f327x_otg.c
  31. 66 11
      src/portable/nordic/nrf5x/dcd_nrf5x.c
  32. 6 0
      src/portable/nuvoton/nuc120/dcd_nuc120.c
  33. 6 0
      src/portable/nuvoton/nuc121/dcd_nuc121.c
  34. 6 0
      src/portable/nuvoton/nuc505/dcd_nuc505.c
  35. 6 0
      src/portable/nxp/khci/dcd_khci.c
  36. 6 0
      src/portable/nxp/lpc17_40/dcd_lpc17_40.c
  37. 6 0
      src/portable/nxp/lpc_ip3511/dcd_lpc_ip3511.c
  38. 6 0
      src/portable/nxp/transdimension/dcd_transdimension.c
  39. 6 0
      src/portable/raspberrypi/rp2040/dcd_rp2040.c
  40. 6 0
      src/portable/renesas/usba/dcd_usba.c
  41. 6 0
      src/portable/silabs/efm32/dcd_efm32.c
  42. 6 0
      src/portable/sony/cxd56/dcd_cxd56.c
  43. 6 0
      src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c
  44. 6 0
      src/portable/st/synopsys/dcd_synopsys.c
  45. 5 0
      src/portable/template/dcd_template.c
  46. 6 0
      src/portable/ti/msp430x5xx/dcd_msp430x5xx.c
  47. 6 0
      src/portable/valentyusb/eptri/dcd_eptri.c

+ 1 - 1
examples/device/audio_4_channel_mic/src/usb_descriptors.c

@@ -92,7 +92,7 @@ enum
 uint8_t const desc_configuration[] =
 {
     // Interface count, string index, total length, attribute, power in mA
-    TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100),
+    TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100),
 
     // Interface number, string index, EP Out & EP In address, EP size
     TUD_AUDIO_MIC_FOUR_CH_DESCRIPTOR(/*_itfnum*/ ITF_NUM_AUDIO_CONTROL, /*_stridx*/ 0, /*_nBytesPerSample*/ CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX, /*_nBitsUsedPerSample*/ CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX*8, /*_epin*/ 0x80 | EPNUM_AUDIO, /*_epsize*/ CFG_TUD_AUDIO_EP_SZ_IN)

+ 1 - 1
examples/device/audio_test/src/usb_descriptors.c

@@ -92,7 +92,7 @@ enum
 uint8_t const desc_configuration[] =
 {
     // Interface count, string index, total length, attribute, power in mA
-    TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100),
+    TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100),
 
     // Interface number, string index, EP Out & EP In address, EP size
     TUD_AUDIO_MIC_ONE_CH_DESCRIPTOR(/*_itfnum*/ ITF_NUM_AUDIO_CONTROL, /*_stridx*/ 0, /*_nBytesPerSample*/ CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX, /*_nBitsUsedPerSample*/ CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX*8, /*_epin*/ 0x80 | EPNUM_AUDIO, /*_epsize*/ CFG_TUD_AUDIO_EP_SZ_IN)

+ 2 - 2
examples/device/cdc_dual_ports/src/usb_descriptors.c

@@ -118,7 +118,7 @@ enum
 uint8_t const desc_fs_configuration[] =
 {
   // Config number, interface count, string index, total length, attribute, power in mA
-  TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100),
+  TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100),
 
   // 1st CDC: Interface number, string index, EP notification address and size, EP data address (out, in) and size.
   TUD_CDC_DESCRIPTOR(ITF_NUM_CDC_0, 4, EPNUM_CDC_0_NOTIF, 8, EPNUM_CDC_0_OUT, EPNUM_CDC_0_IN, 64),
@@ -131,7 +131,7 @@ uint8_t const desc_fs_configuration[] =
 uint8_t const desc_hs_configuration[] =
 {
   // Config number, interface count, string index, total length, attribute, power in mA
-  TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100),
+  TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100),
 
   // 1st CDC: Interface number, string index, EP notification address and size, EP data address (out, in) and size.
   TUD_CDC_DESCRIPTOR(ITF_NUM_CDC_0, 4, EPNUM_CDC_0_NOTIF, 8, EPNUM_CDC_0_OUT, EPNUM_CDC_0_IN, 512),

+ 18 - 1
examples/device/cdc_msc/src/msc_disk.c

@@ -188,18 +188,35 @@ int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset, void* buff
 {
   (void) lun;
 
+  // out of ramdisk
+  if ( lba >= DISK_BLOCK_NUM ) return -1;
+
   uint8_t const* addr = msc_disk[lba] + offset;
   memcpy(buffer, addr, bufsize);
 
   return bufsize;
 }
 
+bool tud_msc_is_writable_cb (uint8_t lun)
+{
+  (void) lun;
+
+#ifdef CFG_EXAMPLE_MSC_READONLY
+  return false;
+#else
+  return true;
+#endif
+}
+
 // Callback invoked when received WRITE10 command.
 // Process data in buffer to disk's storage and return number of written bytes
 int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset, uint8_t* buffer, uint32_t bufsize)
 {
   (void) lun;
 
+  // out of ramdisk
+  if ( lba >= DISK_BLOCK_NUM ) return -1;
+
 #ifndef CFG_EXAMPLE_MSC_READONLY
   uint8_t* addr = msc_disk[lba] + offset;
   memcpy(addr, buffer, bufsize);
@@ -218,7 +235,7 @@ int32_t tud_msc_scsi_cb (uint8_t lun, uint8_t const scsi_cmd[16], void* buffer,
   // read10 & write10 has their own callback and MUST not be handled here
 
   void const* response = NULL;
-  uint16_t resplen = 0;
+  int32_t resplen = 0;
 
   // most scsi handled is input
   bool in_xfer = true;

+ 2 - 2
examples/device/cdc_msc/src/usb_descriptors.c

@@ -129,7 +129,7 @@ enum
 uint8_t const desc_fs_configuration[] =
 {
   // Config number, interface count, string index, total length, attribute, power in mA
-  TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100),
+  TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100),
 
   // Interface number, string index, EP notification address and size, EP data address (out, in) and size.
   TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 4, EPNUM_CDC_NOTIF, 8, EPNUM_CDC_OUT, EPNUM_CDC_IN, 64),
@@ -142,7 +142,7 @@ uint8_t const desc_fs_configuration[] =
 uint8_t const desc_hs_configuration[] =
 {
   // Config number, interface count, string index, total length, attribute, power in mA
-  TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100),
+  TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100),
 
   // Interface number, string index, EP notification address and size, EP data address (out, in) and size.
   TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 4, EPNUM_CDC_NOTIF, 8, EPNUM_CDC_OUT, EPNUM_CDC_IN, 512),

+ 7 - 1
examples/device/cdc_msc_freertos/src/msc_disk.c

@@ -178,6 +178,9 @@ int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset, void* buff
 {
   (void) lun;
 
+  // out of ramdisk
+  if ( lba >= DISK_BLOCK_NUM ) return -1;
+
   uint8_t const* addr = msc_disk[lba] + offset;
   memcpy(buffer, addr, bufsize);
 
@@ -190,6 +193,9 @@ int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset, uint8_t*
 {
   (void) lun;
 
+  // out of ramdisk
+  if ( lba >= DISK_BLOCK_NUM ) return -1;
+
 #ifndef CFG_EXAMPLE_MSC_READONLY
   uint8_t* addr = msc_disk[lba] + offset;
   memcpy(addr, buffer, bufsize);
@@ -208,7 +214,7 @@ int32_t tud_msc_scsi_cb (uint8_t lun, uint8_t const scsi_cmd[16], void* buffer,
   // read10 & write10 has their own callback and MUST not be handled here
 
   void const* response = NULL;
-  uint16_t resplen = 0;
+  int32_t resplen = 0;
 
   // most scsi handled is input
   bool in_xfer = true;

+ 2 - 2
examples/device/cdc_msc_freertos/src/usb_descriptors.c

@@ -117,7 +117,7 @@ enum
 uint8_t const desc_fs_configuration[] =
 {
   // Config number, interface count, string index, total length, attribute, power in mA
-  TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100),
+  TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100),
 
   // Interface number, string index, EP notification address and size, EP data address (out, in) and size.
   TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 4, EPNUM_CDC_NOTIF, 8, EPNUM_CDC_OUT, EPNUM_CDC_IN, 64),
@@ -130,7 +130,7 @@ uint8_t const desc_fs_configuration[] =
 uint8_t const desc_hs_configuration[] =
 {
   // Config number, interface count, string index, total length, attribute, power in mA
-  TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100),
+  TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100),
 
   // Interface number, string index, EP notification address and size, EP data address (out, in) and size.
   TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 4, EPNUM_CDC_NOTIF, 8, EPNUM_CDC_OUT, EPNUM_CDC_IN, 512),

+ 1 - 1
examples/device/dfu/src/usb_descriptors.c

@@ -97,7 +97,7 @@ enum
 uint8_t const desc_configuration[] =
 {
   // Config number, interface count, string index, total length, attribute, power in mA
-  TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100),
+  TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100),
 
   // Interface number, Alternate count, starting string index, attributes, detach timeout, transfer size
   TUD_DFU_DESCRIPTOR(ITF_NUM_DFU_MODE, ALT_COUNT, 4, FUNC_ATTRS, 1000, CFG_TUD_DFU_XFER_BUFSIZE),

+ 1 - 1
examples/device/dfu_runtime/src/usb_descriptors.c

@@ -92,7 +92,7 @@ enum
 uint8_t const desc_configuration[] =
 {
   // Config number, interface count, string index, total length, attribute, power in mA
-  TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100),
+  TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100),
 
   // Interface number, string index, attributes, detach timeout, transfer size */
   TUD_DFU_RT_DESCRIPTOR(ITF_NUM_DFU_RT, 4, 0x0d, 1000, 4096),

+ 7 - 1
examples/device/dynamic_configuration/src/msc_disk.c

@@ -178,6 +178,9 @@ int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset, void* buff
 {
   (void) lun;
 
+  // out of ramdisk
+  if ( lba >= DISK_BLOCK_NUM ) return -1;
+
   uint8_t const* addr = msc_disk[lba] + offset;
   memcpy(buffer, addr, bufsize);
 
@@ -190,6 +193,9 @@ int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset, uint8_t*
 {
   (void) lun;
 
+  // out of ramdisk
+  if ( lba >= DISK_BLOCK_NUM ) return -1;
+
 #ifndef CFG_EXAMPLE_MSC_READONLY
   uint8_t* addr = msc_disk[lba] + offset;
   memcpy(addr, buffer, bufsize);
@@ -208,7 +214,7 @@ int32_t tud_msc_scsi_cb (uint8_t lun, uint8_t const scsi_cmd[16], void* buffer,
   // read10 & write10 has their own callback and MUST not be handled here
 
   void const* response = NULL;
-  uint16_t resplen = 0;
+  int32_t resplen = 0;
 
   // most scsi handled is input
   bool in_xfer = true;

+ 2 - 2
examples/device/dynamic_configuration/src/usb_descriptors.c

@@ -160,7 +160,7 @@ enum
 uint8_t const desc_configuration_0[] =
 {
   // Config number, interface count, string index, total length, attribute, power in mA
-  TUD_CONFIG_DESCRIPTOR(1, ITF_0_NUM_TOTAL, 0, CONFIG_0_TOTAL_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100),
+  TUD_CONFIG_DESCRIPTOR(1, ITF_0_NUM_TOTAL, 0, CONFIG_0_TOTAL_LEN, 0x00, 100),
 
   // Interface number, string index, EP notification address and size, EP data address (out, in) and size.
   TUD_CDC_DESCRIPTOR(ITF_0_NUM_CDC, 0, EPNUM_0_CDC_NOTIF, 8, EPNUM_0_CDC_OUT, EPNUM_0_CDC_IN, 64),
@@ -173,7 +173,7 @@ uint8_t const desc_configuration_0[] =
 uint8_t const desc_configuraiton_1[] =
 {
   // Config number, interface count, string index, total length, attribute, power in mA
-  TUD_CONFIG_DESCRIPTOR(1, ITF_1_NUM_TOTAL, 0, CONFIG_1_TOTAL_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100),
+  TUD_CONFIG_DESCRIPTOR(1, ITF_1_NUM_TOTAL, 0, CONFIG_1_TOTAL_LEN, 0x00, 100),
 
   // Interface number, string index, EP Out & EP In address, EP size
   TUD_MSC_DESCRIPTOR(ITF_1_NUM_MSC, 0, EPNUM_1_MSC_OUT, EPNUM_1_MSC_IN, TUD_OPT_HIGH_SPEED ? 512 : 64),

+ 1 - 1
examples/device/hid_generic_inout/src/usb_descriptors.c

@@ -101,7 +101,7 @@ enum
 uint8_t const desc_configuration[] =
 {
   // Config number, interface count, string index, total length, attribute, power in mA
-  TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100),
+  TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100),
 
   // Interface number, string index, protocol, report descriptor len, EP In & Out address, size & polling interval
   TUD_HID_INOUT_DESCRIPTOR(ITF_NUM_HID, 0, HID_ITF_PROTOCOL_NONE, sizeof(desc_hid_report), EPNUM_HID, 0x80 | EPNUM_HID, CFG_TUD_HID_EP_BUFSIZE, 10)

+ 2 - 2
examples/device/midi_test/src/usb_descriptors.c

@@ -91,7 +91,7 @@ enum
 uint8_t const desc_fs_configuration[] =
 {
   // Config number, interface count, string index, total length, attribute, power in mA
-  TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100),
+  TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100),
 
   // Interface number, string index, EP Out & EP In address, EP size
   TUD_MIDI_DESCRIPTOR(ITF_NUM_MIDI, 0, EPNUM_MIDI, 0x80 | EPNUM_MIDI, 64)
@@ -101,7 +101,7 @@ uint8_t const desc_fs_configuration[] =
 uint8_t const desc_hs_configuration[] =
 {
   // Config number, interface count, string index, total length, attribute, power in mA
-  TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100),
+  TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100),
 
   // Interface number, string index, EP Out & EP In address, EP size
   TUD_MIDI_DESCRIPTOR(ITF_NUM_MIDI, 0, EPNUM_MIDI, 0x80 | EPNUM_MIDI, 512)

+ 18 - 1
examples/device/msc_dual_lun/src/msc_disk_dual.c

@@ -268,16 +268,33 @@ bool tud_msc_start_stop_cb(uint8_t lun, uint8_t power_condition, bool start, boo
 // Copy disk's data to buffer (up to bufsize) and return number of copied bytes.
 int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset, void* buffer, uint32_t bufsize)
 {
+  // out of ramdisk
+  if ( lba >= DISK_BLOCK_NUM ) return -1;
+
   uint8_t const* addr = (lun ? msc_disk1[lba] : msc_disk0[lba]) + offset;
   memcpy(buffer, addr, bufsize);
 
   return bufsize;
 }
 
+bool tud_msc_is_writable_cb (uint8_t lun)
+{
+  (void) lun;
+
+#ifdef CFG_EXAMPLE_MSC_READONLY
+  return false;
+#else
+  return true;
+#endif
+}
+
 // Callback invoked when received WRITE10 command.
 // Process data in buffer to disk's storage and return number of written bytes
 int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset, uint8_t* buffer, uint32_t bufsize)
 {
+  // out of ramdisk
+  if ( lba >= DISK_BLOCK_NUM ) return -1;
+
 #ifndef CFG_EXAMPLE_MSC_READONLY
   uint8_t* addr = (lun ? msc_disk1[lba] : msc_disk0[lba])  + offset;
   memcpy(addr, buffer, bufsize);
@@ -296,7 +313,7 @@ int32_t tud_msc_scsi_cb (uint8_t lun, uint8_t const scsi_cmd[16], void* buffer,
   // read10 & write10 has their own callback and MUST not be handled here
 
   void const* response = NULL;
-  uint16_t resplen = 0;
+  int32_t resplen = 0;
 
   // most scsi handled is input
   bool in_xfer = true;

+ 2 - 2
examples/device/msc_dual_lun/src/usb_descriptors.c

@@ -99,7 +99,7 @@ enum
 uint8_t const desc_fs_configuration[] =
 {
   // Config number, interface count, string index, total length, attribute, power in mA
-  TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100),
+  TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100),
 
   // Interface number, string index, EP Out & EP In address, EP size
   TUD_MSC_DESCRIPTOR(ITF_NUM_MSC, 0, EPNUM_MSC_OUT, EPNUM_MSC_IN, 64),
@@ -109,7 +109,7 @@ uint8_t const desc_fs_configuration[] =
 uint8_t const desc_hs_configuration[] =
 {
   // Config number, interface count, string index, total length, attribute, power in mA
-  TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100),
+  TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100),
 
   // Interface number, string index, EP Out & EP In address, EP size
   TUD_MSC_DESCRIPTOR(ITF_NUM_MSC, 0, EPNUM_MSC_OUT, EPNUM_MSC_IN, 512),

+ 1 - 1
examples/device/uac2_headset/src/usb_descriptors.c

@@ -98,7 +98,7 @@ uint8_t const * tud_descriptor_device_cb(void)
 uint8_t const desc_configuration[] =
 {
     // Interface count, string index, total length, attribute, power in mA
-    TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100),
+    TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100),
 
     // Interface number, string index, EP Out & EP In address, EP size
     TUD_AUDIO_HEADSET_STEREO_DESCRIPTOR(2, EPNUM_AUDIO_OUT, EPNUM_AUDIO_IN | 0x80)

+ 1 - 1
examples/device/usbtmc/src/usb_descriptors.c

@@ -122,7 +122,7 @@ enum
 uint8_t const desc_configuration[] =
 {
   // Config number, interface count, string index, total length, attribute, power in mA
-  TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100),
+  TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100),
 
   TUD_USBTMC_DESC(ITF_NUM_USBTMC),
 };

+ 1 - 1
examples/device/webusb_serial/src/usb_descriptors.c

@@ -107,7 +107,7 @@ enum
 uint8_t const desc_configuration[] =
 {
   // Config number, interface count, string index, total length, attribute, power in mA
-  TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100),
+  TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100),
 
   // Interface number, string index, EP notification address and size, EP data address (out, in) and size.
   TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 4, 0x81, 8, EPNUM_CDC_OUT, 0x80 | EPNUM_CDC_IN, TUD_OPT_HIGH_SPEED ? 512 : 64),

+ 10 - 10
src/class/hid/hid_device.h

@@ -195,16 +195,7 @@ static inline bool  tud_hid_gamepad_report(uint8_t report_id, int8_t x, int8_t y
       HID_REPORT_COUNT ( 1                                      )  ,\
       HID_REPORT_SIZE  ( 8                                      )  ,\
       HID_INPUT        ( HID_CONSTANT                           )  ,\
-    /* 6-byte Keycodes */ \
-    HID_USAGE_PAGE ( HID_USAGE_PAGE_KEYBOARD )                     ,\
-      HID_USAGE_MIN    ( 0                                   )     ,\
-      HID_USAGE_MAX_N  ( 255, 2                              )     ,\
-      HID_LOGICAL_MIN  ( 0                                   )     ,\
-      HID_LOGICAL_MAX_N( 255, 2                              )     ,\
-      HID_REPORT_COUNT ( 6                                   )     ,\
-      HID_REPORT_SIZE  ( 8                                   )     ,\
-      HID_INPUT        ( HID_DATA | HID_ARRAY | HID_ABSOLUTE )     ,\
-    /* 5-bit LED Indicator Kana | Compose | ScrollLock | CapsLock | NumLock */ \
+    /* Output 5-bit LED Indicator Kana | Compose | ScrollLock | CapsLock | NumLock */ \
     HID_USAGE_PAGE  ( HID_USAGE_PAGE_LED                   )       ,\
       HID_USAGE_MIN    ( 1                                       ) ,\
       HID_USAGE_MAX    ( 5                                       ) ,\
@@ -215,6 +206,15 @@ static inline bool  tud_hid_gamepad_report(uint8_t report_id, int8_t x, int8_t y
       HID_REPORT_COUNT ( 1                                       ) ,\
       HID_REPORT_SIZE  ( 3                                       ) ,\
       HID_OUTPUT       ( HID_CONSTANT                            ) ,\
+    /* 6-byte Keycodes */ \
+    HID_USAGE_PAGE ( HID_USAGE_PAGE_KEYBOARD )                     ,\
+      HID_USAGE_MIN    ( 0                                   )     ,\
+      HID_USAGE_MAX_N  ( 255, 2                              )     ,\
+      HID_LOGICAL_MIN  ( 0                                   )     ,\
+      HID_LOGICAL_MAX_N( 255, 2                              )     ,\
+      HID_REPORT_COUNT ( 6                                   )     ,\
+      HID_REPORT_SIZE  ( 8                                   )     ,\
+      HID_INPUT        ( HID_DATA | HID_ARRAY | HID_ABSOLUTE )     ,\
   HID_COLLECTION_END \
 
 // Mouse Report Descriptor Template

+ 380 - 166
src/class/msc/msc_device.c

@@ -46,7 +46,8 @@ enum
   MSC_STAGE_CMD  = 0,
   MSC_STAGE_DATA,
   MSC_STAGE_STATUS,
-  MSC_STAGE_STATUS_SENT
+  MSC_STAGE_STATUS_SENT,
+  MSC_STAGE_NEED_RESET,
 };
 
 typedef struct
@@ -61,7 +62,7 @@ typedef struct
 
   // Bulk Only Transfer (BOT) Protocol
   uint8_t  stage;
-  uint32_t total_len;
+  uint32_t total_len;   // byte to be transferred, can be smaller than total_bytes in cbw
   uint32_t xferred_len; // numbered of bytes transferred so far in the Data Stage
 
   // Sense Response Data
@@ -78,7 +79,55 @@ CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN static uint8_t _mscd_buf[CFG_TUD_MSC_EP_
 //--------------------------------------------------------------------+
 static int32_t proc_builtin_scsi(uint8_t lun, uint8_t const scsi_cmd[16], uint8_t* buffer, uint32_t bufsize);
 static void proc_read10_cmd(uint8_t rhport, mscd_interface_t* p_msc);
+
 static void proc_write10_cmd(uint8_t rhport, mscd_interface_t* p_msc);
+static void proc_write10_new_data(uint8_t rhport, mscd_interface_t* p_msc, uint32_t xferred_bytes);
+
+TU_ATTR_ALWAYS_INLINE static inline bool is_data_in(uint8_t dir)
+{
+  return tu_bit_test(dir, 7);
+}
+
+static inline bool send_csw(uint8_t rhport, mscd_interface_t* p_msc)
+{
+  // Data residue is always = host expect - actual transferred
+  p_msc->csw.data_residue = p_msc->cbw.total_bytes - p_msc->xferred_len;
+
+  p_msc->stage = MSC_STAGE_STATUS_SENT;
+  return usbd_edpt_xfer(rhport, p_msc->ep_in , (uint8_t*) &p_msc->csw, sizeof(msc_csw_t));
+}
+
+static inline bool prepare_cbw(uint8_t rhport, mscd_interface_t* p_msc)
+{
+  p_msc->stage = MSC_STAGE_CMD;
+  return usbd_edpt_xfer(rhport, p_msc->ep_out, (uint8_t*) &p_msc->cbw, sizeof(msc_cbw_t));
+}
+
+static void fail_scsi_op(uint8_t rhport, mscd_interface_t* p_msc, uint8_t status)
+{
+  msc_cbw_t const * p_cbw = &p_msc->cbw;
+  msc_csw_t       * p_csw = &p_msc->csw;
+
+  p_csw->status       = status;
+  p_csw->data_residue = p_msc->cbw.total_bytes - p_msc->xferred_len;
+  p_msc->stage        = MSC_STAGE_STATUS;
+
+  // failed but sense key is not set: default to Illegal Request
+  if ( p_msc->sense_key == 0 ) tud_msc_set_sense(p_cbw->lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00);
+
+  // If there is data stage and not yet complete, stall it
+  if ( p_cbw->total_bytes && p_csw->data_residue )
+  {
+    if ( is_data_in(p_cbw->dir) )
+    {
+      usbd_edpt_stall(rhport, p_msc->ep_in);
+    }
+    else
+    {
+      usbd_edpt_stall(rhport, p_msc->ep_out);
+    }
+  }
+}
 
 static inline uint32_t rdwr10_get_lba(uint8_t const command[])
 {
@@ -89,15 +138,60 @@ static inline uint32_t rdwr10_get_lba(uint8_t const command[])
   return tu_ntohl(lba);
 }
 
-static inline uint16_t rdwr10_get_blockcount(uint8_t const command[])
+static inline uint16_t rdwr10_get_blockcount(msc_cbw_t const* cbw)
 {
-  // use offsetof to avoid pointer to the odd/misaligned address
-  uint16_t const block_count = tu_unaligned_read16(command + offsetof(scsi_write10_t, block_count));
-
-  // block count is in Big Endian
+  uint16_t const block_count = tu_unaligned_read16(cbw->command + offsetof(scsi_write10_t, block_count));
   return tu_ntohs(block_count);
 }
 
+static inline uint16_t rdwr10_get_blocksize(msc_cbw_t const* cbw)
+{
+  // first extract block count in the command
+  uint16_t const block_count = rdwr10_get_blockcount(cbw);
+
+  // invalid block count
+  if (block_count == 0) return 0;
+
+  return cbw->total_bytes / block_count;
+}
+
+uint8_t rdwr10_validate_cmd(msc_cbw_t const* cbw)
+{
+  uint8_t status = MSC_CSW_STATUS_PASSED;
+  uint16_t const block_count = rdwr10_get_blockcount(cbw);
+
+  if ( cbw->total_bytes == 0 )
+  {
+    if ( block_count )
+    {
+      TU_LOG(MSC_DEBUG, "  SCSI case 2 (Hn < Di) or case 3 (Hn < Do) \r\n");
+      status = MSC_CSW_STATUS_PHASE_ERROR;
+    }else
+    {
+      // no data transfer, only exist in complaint test suite
+    }
+  }else
+  {
+    if ( SCSI_CMD_READ_10 == cbw->command[0] && !is_data_in(cbw->dir) )
+    {
+      TU_LOG(MSC_DEBUG, "  SCSI case 10 (Ho <> Di)\r\n");
+      status = MSC_CSW_STATUS_PHASE_ERROR;
+    }
+    else if ( SCSI_CMD_WRITE_10 == cbw->command[0] && is_data_in(cbw->dir) )
+    {
+      TU_LOG(MSC_DEBUG, "  SCSI case 8 (Hi <> Do)\r\n");
+      status = MSC_CSW_STATUS_PHASE_ERROR;
+    }
+    else if ( !block_count )
+    {
+      TU_LOG(MSC_DEBUG, "  SCSI case 4 Hi > Dn\r\n");
+      status =  MSC_CSW_STATUS_FAILED;
+    }
+  }
+
+  return status;
+}
+
 //--------------------------------------------------------------------+
 // Debug
 //--------------------------------------------------------------------+
@@ -174,35 +268,89 @@ uint16_t mscd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint1
   TU_ASSERT( usbd_open_edpt_pair(rhport, tu_desc_next(itf_desc), 2, TUSB_XFER_BULK, &p_msc->ep_out, &p_msc->ep_in), 0 );
 
   // Prepare for Command Block Wrapper
-  if ( !usbd_edpt_xfer(rhport, p_msc->ep_out, (uint8_t*) &p_msc->cbw, sizeof(msc_cbw_t)) )
-  {
-    TU_LOG_FAILED();
-    TU_BREAKPOINT();
-  }
+  TU_ASSERT( prepare_cbw(rhport, p_msc), drv_len);
 
   return drv_len;
 }
 
+static void proc_bot_reset(mscd_interface_t* p_msc)
+{
+  p_msc->stage       = MSC_STAGE_CMD;
+  p_msc->total_len   = 0;
+  p_msc->xferred_len = 0;
+
+  p_msc->sense_key           = 0;
+  p_msc->add_sense_code      = 0;
+  p_msc->add_sense_qualifier = 0;
+}
+
 // Invoked when a control transfer occurred on an interface of this class
 // Driver response accordingly to the request and the transfer stage (setup/data/ack)
 // return false to stall control endpoint (e.g unsupported request)
-bool mscd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * p_request)
+bool mscd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request)
 {
   // nothing to do with DATA & ACK stage
   if (stage != CONTROL_STAGE_SETUP) return true;
 
-  // Handle class request only
-  TU_VERIFY(p_request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS);
+  mscd_interface_t* p_msc = &_mscd_itf;
 
-  switch ( p_request->bRequest )
+  // Clear Endpoint Feature (stall) for recovery
+  if ( TUSB_REQ_TYPE_STANDARD     == request->bmRequestType_bit.type      &&
+       TUSB_REQ_RCPT_ENDPOINT     == request->bmRequestType_bit.recipient &&
+       TUSB_REQ_CLEAR_FEATURE     == request->bRequest                    &&
+       TUSB_REQ_FEATURE_EDPT_HALT == request->wValue )
+  {
+    uint8_t const ep_addr = tu_u16_low(request->wIndex);
+
+    if ( p_msc->stage == MSC_STAGE_NEED_RESET )
+    {
+      // reset recovery is required to recover from this stage
+      // Clear Stall request cannot resolve this -> continue to stall endpoint
+      usbd_edpt_stall(rhport, ep_addr);
+    }
+    else
+    {
+      if ( ep_addr == p_msc->ep_in )
+      {
+        if ( p_msc->stage == MSC_STAGE_STATUS )
+        {
+          // resume sending SCSI status if we are in this stage previously before stalled
+          TU_ASSERT( send_csw(rhport, p_msc) );
+        }
+      }
+      else if ( ep_addr == p_msc->ep_out )
+      {
+        if ( p_msc->stage == MSC_STAGE_CMD )
+        {
+          // part of reset recovery (probably due to invalid CBW) -> prepare for new command
+          TU_ASSERT( prepare_cbw(rhport, p_msc) );
+        }
+      }
+    }
+
+    return true;
+  }
+
+  // From this point only handle class request only
+  TU_VERIFY(request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS);
+
+  switch ( request->bRequest )
   {
     case MSC_REQ_RESET:
-      // TODO: Actually reset interface.
-      tud_control_status(rhport, p_request);
+      TU_LOG(MSC_DEBUG, "  MSC BOT Reset\r\n");
+      TU_VERIFY(request->wValue == 0 && request->wLength == 0);
+
+      // driver state reset
+      proc_bot_reset(p_msc);
+
+      tud_control_status(rhport, request);
     break;
 
     case MSC_REQ_GET_MAX_LUN:
     {
+      TU_LOG(MSC_DEBUG, "  MSC Get Max Lun\r\n");
+      TU_VERIFY(request->wValue == 0 && request->wLength == 1);
+
       uint8_t maxlun = 1;
       if (tud_msc_get_maxlun_cb) maxlun = tud_msc_get_maxlun_cb();
       TU_VERIFY(maxlun);
@@ -210,7 +358,7 @@ bool mscd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t
       // MAX LUN is minus 1 by specs
       maxlun--;
 
-      tud_control_xfer(rhport, p_request, &maxlun, 1);
+      tud_control_xfer(rhport, request, &maxlun, 1);
     }
     break;
 
@@ -222,6 +370,8 @@ bool mscd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t
 
 bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes)
 {
+  (void) event;
+
   mscd_interface_t* p_msc = &_mscd_itf;
   msc_cbw_t const * p_cbw = &p_msc->cbw;
   msc_csw_t       * p_csw = &p_msc->csw;
@@ -233,46 +383,79 @@ bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t
       // Complete IN while waiting for CMD is usually Status of previous SCSI op, ignore it
       if(ep_addr != p_msc->ep_out) return true;
 
-      TU_ASSERT( event == XFER_RESULT_SUCCESS &&
-                 xferred_bytes == sizeof(msc_cbw_t) && p_cbw->signature == MSC_CBW_SIGNATURE );
+      if ( !(xferred_bytes == sizeof(msc_cbw_t) && p_cbw->signature == MSC_CBW_SIGNATURE) )
+      {
+        TU_LOG(MSC_DEBUG, "  SCSI CBW is not valid\r\n");
+
+        // BOT 6.6.1 If CBW is not valid stall both endpoints until reset recovery
+        p_msc->stage = MSC_STAGE_NEED_RESET;
+
+        // invalid CBW stall both endpoints
+        usbd_edpt_stall(rhport, p_msc->ep_in);
+        usbd_edpt_stall(rhport, p_msc->ep_out);
+
+        return false;
+      }
 
       TU_LOG(MSC_DEBUG, "  SCSI Command: %s\r\n", tu_lookup_find(&_msc_scsi_cmd_table, p_cbw->command[0]));
-      // TU_LOG_MEM(MSC_DEBUG, p_cbw, xferred_bytes, 2);
+      //TU_LOG_MEM(MSC_DEBUG, p_cbw, xferred_bytes, 2);
 
       p_csw->signature    = MSC_CSW_SIGNATURE;
       p_csw->tag          = p_cbw->tag;
       p_csw->data_residue = 0;
+      p_csw->status       = MSC_CSW_STATUS_PASSED;
 
       /*------------- Parse command and prepare DATA -------------*/
       p_msc->stage = MSC_STAGE_DATA;
       p_msc->total_len = p_cbw->total_bytes;
       p_msc->xferred_len = 0;
 
-      if (SCSI_CMD_READ_10 == p_cbw->command[0])
+      // Read10 or Write10
+      if ( (SCSI_CMD_READ_10 == p_cbw->command[0]) || (SCSI_CMD_WRITE_10 == p_cbw->command[0]) )
       {
-        proc_read10_cmd(rhport, p_msc);
-      }
-      else if (SCSI_CMD_WRITE_10 == p_cbw->command[0])
-      {
-        proc_write10_cmd(rhport, p_msc);
+        uint8_t const status = rdwr10_validate_cmd(p_cbw);
+
+        if ( status != MSC_CSW_STATUS_PASSED)
+        {
+          fail_scsi_op(rhport, p_msc, status);
+        }else if ( p_cbw->total_bytes )
+        {
+          if (SCSI_CMD_READ_10 == p_cbw->command[0])
+          {
+            proc_read10_cmd(rhport, p_msc);
+          }else
+          {
+            proc_write10_cmd(rhport, p_msc);
+          }
+        }else
+        {
+          // no data transfer, only exist in complaint test suite
+          p_msc->stage = MSC_STAGE_STATUS;
+        }
       }
       else
       {
         // For other SCSI commands
         // 1. OUT : queue transfer (invoke app callback after done)
         // 2. IN & Zero: Process if is built-in, else Invoke app callback. Skip DATA if zero length
-        if ( (p_cbw->total_bytes > 0 ) && !tu_bit_test(p_cbw->dir, 7) )
+        if ( (p_cbw->total_bytes > 0 ) && !is_data_in(p_cbw->dir) )
         {
-          // queue transfer
-          TU_ASSERT( usbd_edpt_xfer(rhport, p_msc->ep_out, _mscd_buf, p_msc->total_len) );
+          if (p_cbw->total_bytes > sizeof(_mscd_buf))
+          {
+            TU_LOG(MSC_DEBUG, "  SCSI reject non READ10/WRITE10 with large data\r\n");
+            fail_scsi_op(rhport, p_msc, MSC_CSW_STATUS_FAILED);
+          }else
+          {
+            // Didn't check for case 9 (Ho > Dn), which requires examining scsi command first
+            // but it is OK to just receive data then responded with failed status
+            TU_ASSERT( usbd_edpt_xfer(rhport, p_msc->ep_out, _mscd_buf, p_msc->total_len) );
+          }
         }else
         {
-          int32_t resplen;
-
           // First process if it is a built-in commands
-          resplen = proc_builtin_scsi(p_cbw->lun, p_cbw->command, _mscd_buf, sizeof(_mscd_buf));
+          int32_t resplen = proc_builtin_scsi(p_cbw->lun, p_cbw->command, _mscd_buf, sizeof(_mscd_buf));
 
-          // Not built-in, invoke user callback
+          // Invoke user callback if not built-in
           if ( (resplen < 0) && (p_msc->sense_key == 0) )
           {
             resplen = tud_msc_scsi_cb(p_cbw->lun, p_cbw->command, _mscd_buf, p_msc->total_len);
@@ -280,30 +463,37 @@ bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t
 
           if ( resplen < 0 )
           {
-            p_msc->total_len = 0;
-            p_csw->status = MSC_CSW_STATUS_FAILED;
-            p_msc->stage = MSC_STAGE_STATUS;
-
-            // failed but senskey is not set: default to Illegal Request
-            if ( p_msc->sense_key == 0 ) tud_msc_set_sense(p_cbw->lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00);
-
-            // Stall bulk In if needed
-            if (p_cbw->total_bytes) usbd_edpt_stall(rhport, p_msc->ep_in);
+            // unsupported command
+            TU_LOG(MSC_DEBUG, "  SCSI unsupported command\r\n");
+            fail_scsi_op(rhport, p_msc, MSC_CSW_STATUS_FAILED);
           }
-          else
+          else if (resplen == 0)
           {
-            p_msc->total_len = (uint32_t) resplen;
-            p_csw->status = MSC_CSW_STATUS_PASSED;
-
-            if (p_msc->total_len)
+            if (p_cbw->total_bytes)
             {
-              TU_ASSERT( p_cbw->total_bytes >= p_msc->total_len ); // cannot return more than host expect
-              TU_ASSERT( usbd_edpt_xfer(rhport, p_msc->ep_in, _mscd_buf, p_msc->total_len) );
+              // 6.7 The 13 Cases: case 4 (Hi > Dn)
+              TU_LOG(MSC_DEBUG, "  SCSI case 4 (Hi > Dn): %lu\r\n", p_cbw->total_bytes);
+              fail_scsi_op(rhport, p_msc, MSC_CSW_STATUS_FAILED);
             }else
             {
+              // case 1 Hn = Dn: all good
               p_msc->stage = MSC_STAGE_STATUS;
             }
           }
+          else
+          {
+            if ( p_cbw->total_bytes == 0 )
+            {
+              // 6.7 The 13 Cases: case 2 (Hn < Di)
+              TU_LOG(MSC_DEBUG, "  SCSI case 2 (Hn < Di): %lu\r\n", p_cbw->total_bytes);
+              fail_scsi_op(rhport, p_msc, MSC_CSW_STATUS_FAILED);
+            }else
+            {
+              // cannot return more than host expect
+              p_msc->total_len = tu_min32((uint32_t) resplen, p_cbw->total_bytes);
+              TU_ASSERT( usbd_edpt_xfer(rhport, p_msc->ep_in, _mscd_buf, p_msc->total_len) );
+            }
+          }
         }
       }
     break;
@@ -312,87 +502,51 @@ bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t
       TU_LOG(MSC_DEBUG, "  SCSI Data\r\n");
       //TU_LOG_MEM(MSC_DEBUG, _mscd_buf, xferred_bytes, 2);
 
-      // OUT transfer, invoke callback if needed
-      if ( !tu_bit_test(p_cbw->dir, 7) )
+      if (SCSI_CMD_READ_10 == p_cbw->command[0])
       {
-        if ( SCSI_CMD_WRITE_10 != p_cbw->command[0] )
-        {
-          int32_t cb_result = tud_msc_scsi_cb(p_cbw->lun, p_cbw->command, _mscd_buf, p_msc->total_len);
+        p_msc->xferred_len += xferred_bytes;
 
-          if ( cb_result < 0 )
-          {
-            p_csw->status = MSC_CSW_STATUS_FAILED;
-            tud_msc_set_sense(p_cbw->lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00); // Sense = Invalid Command Operation
-          }else
-          {
-            p_csw->status = MSC_CSW_STATUS_PASSED;
-          }
-        }
-        else
+        if ( p_msc->xferred_len >= p_msc->total_len )
         {
-          uint16_t const block_sz = p_cbw->total_bytes / rdwr10_get_blockcount(p_cbw->command);
-
-          // Adjust lba with transferred bytes
-          uint32_t const lba = rdwr10_get_lba(p_cbw->command) + (p_msc->xferred_len / block_sz);
+          // Data Stage is complete
+          p_msc->stage = MSC_STAGE_STATUS;
+        }else
+        {
+          proc_read10_cmd(rhport, p_msc);
+        }
+      }
+      else if (SCSI_CMD_WRITE_10 == p_cbw->command[0])
+      {
+        proc_write10_new_data(rhport, p_msc, xferred_bytes);
+      }
+      else
+      {
+        p_msc->xferred_len += xferred_bytes;
 
-          // Application can consume smaller bytes
-          int32_t nbytes = tud_msc_write10_cb(p_cbw->lun, lba, p_msc->xferred_len % block_sz, _mscd_buf, xferred_bytes);
+        // OUT transfer, invoke callback if needed
+        if ( !is_data_in(p_cbw->dir) )
+        {
+          int32_t cb_result = tud_msc_scsi_cb(p_cbw->lun, p_cbw->command, _mscd_buf, p_msc->total_len);
 
-          if ( nbytes < 0 )
+          if ( cb_result < 0 )
           {
-            // negative means error -> skip to status phase, status in CSW set to failed
-            p_csw->data_residue = p_cbw->total_bytes - p_msc->xferred_len;
-            p_csw->status       = MSC_CSW_STATUS_FAILED;
-            p_msc->stage        = MSC_STAGE_STATUS;
-
-            tud_msc_set_sense(p_cbw->lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00); // Sense = Invalid Command Operation
-            break;
+            // unsupported command
+            TU_LOG(MSC_DEBUG, "  SCSI unsupported command\r\n");
+            fail_scsi_op(rhport, p_msc, MSC_CSW_STATUS_FAILED);
           }else
           {
-            // Application consume less than what we got (including zero)
-            if ( nbytes < (int32_t) xferred_bytes )
-            {
-              if ( nbytes > 0 )
-              {
-                p_msc->xferred_len += nbytes;
-                memmove(_mscd_buf, _mscd_buf+nbytes, xferred_bytes-nbytes);
-              }
-
-              // simulate an transfer complete with adjusted parameters --> this driver callback will fired again
-              dcd_event_xfer_complete(rhport, p_msc->ep_out, xferred_bytes-nbytes, XFER_RESULT_SUCCESS, false);
-
-              return true; // skip the rest
-            }
-            else
-            {
-              // Application consume all bytes in our buffer. Nothing to do, process with normal flow
-            }
+            // TODO haven't implement this scenario any further yet
           }
         }
-      }
-
-      // Accumulate data so far
-      p_msc->xferred_len += xferred_bytes;
 
-      if ( p_msc->xferred_len >= p_msc->total_len )
-      {
-        // Data Stage is complete
-        p_msc->stage = MSC_STAGE_STATUS;
-      }
-      else
-      {
-        // READ10 & WRITE10 Can be executed with large bulk of data e.g write 8K bytes (several flash write)
-        // We break it into multiple smaller command whose data size is up to CFG_TUD_MSC_EP_BUFSIZE
-        if (SCSI_CMD_READ_10 == p_cbw->command[0])
+        if ( p_msc->xferred_len >= p_msc->total_len )
         {
-          proc_read10_cmd(rhport, p_msc);
+          // Data Stage is complete
+          p_msc->stage = MSC_STAGE_STATUS;
         }
-        else if (SCSI_CMD_WRITE_10 == p_cbw->command[0])
-        {
-          proc_write10_cmd(rhport, p_msc);
-        }else
+        else
         {
-          // No other command take more than one transfer yet -> unlikely error
+          // This scenario with command that take more than one transfer is already rejected at Command stage
           TU_BREAKPOINT();
         }
       }
@@ -406,7 +560,7 @@ bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t
       // Wait for the Status phase to complete
       if( (ep_addr == p_msc->ep_in) && (xferred_bytes == sizeof(msc_csw_t)) )
       {
-        TU_LOG(MSC_DEBUG, "  SCSI Status: %u\r\n", p_csw->status);
+        TU_LOG(MSC_DEBUG, "  SCSI Status = %u\r\n", p_csw->status);
         // TU_LOG_MEM(MSC_DEBUG, p_csw, xferred_bytes, 2);
 
         // Invoke complete callback if defined
@@ -427,11 +581,11 @@ bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t
           break;
         }
 
-        // Move to default CMD stage
-        p_msc->stage = MSC_STAGE_CMD;
-
-        // Queue for the next CBW
-        TU_ASSERT( usbd_edpt_xfer(rhport, p_msc->ep_out, (uint8_t*) &p_msc->cbw, sizeof(msc_cbw_t)) );
+        TU_ASSERT( prepare_cbw(rhport, p_msc) );
+      }else
+      {
+        // Any xfer ended here is consider unknown error, ignore it
+        TU_LOG1("  Warning expect SCSI Status but received unknown data\r\n");
       }
     break;
 
@@ -440,21 +594,18 @@ bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t
 
   if ( p_msc->stage == MSC_STAGE_STATUS )
   {
-    // Either endpoints is stalled, need to wait until it is cleared by host
-    if ( usbd_edpt_stalled(rhport,  p_msc->ep_in) || usbd_edpt_stalled(rhport,  p_msc->ep_out) )
-    {
-      // simulate an transfer complete with adjusted parameters --> this driver callback will fired again
-      // and response with status phase after halted endpoints are cleared.
-      // note: use ep_out to prevent confusing with STATUS complete
-      dcd_event_xfer_complete(rhport, p_msc->ep_out, 0, XFER_RESULT_SUCCESS, false);
-    }
-    else
+    // skip status if epin is currently stalled, will do it when received Clear Stall request
+    if ( !usbd_edpt_stalled(rhport,  p_msc->ep_in) )
     {
-      // Move to Status Sent stage
-      p_msc->stage = MSC_STAGE_STATUS_SENT;
-
-      // Send SCSI Status
-      TU_ASSERT(usbd_edpt_xfer(rhport, p_msc->ep_in , (uint8_t*) &p_msc->csw, sizeof(msc_csw_t)));
+      if ( (p_cbw->total_bytes > p_msc->xferred_len) && is_data_in(p_cbw->dir) )
+      {
+        // 6.7 The 13 Cases: case 5 (Hi > Di): STALL before status
+        TU_LOG(MSC_DEBUG, "  SCSI case 5 (Hi > Di): %lu > %lu\r\n", p_cbw->total_bytes, p_msc->xferred_len);
+        usbd_edpt_stall(rhport, p_msc->ep_in);
+      }else
+      {
+        TU_ASSERT( send_csw(rhport, p_msc) );
+      }
     }
   }
 
@@ -472,6 +623,8 @@ static int32_t proc_builtin_scsi(uint8_t lun, uint8_t const scsi_cmd[16], uint8_
   (void) bufsize; // TODO refractor later
   int32_t resplen;
 
+  mscd_interface_t* p_msc = &_mscd_itf;
+
   switch ( scsi_cmd[0] )
   {
     case SCSI_CMD_TEST_UNIT_READY:
@@ -482,7 +635,7 @@ static int32_t proc_builtin_scsi(uint8_t lun, uint8_t const scsi_cmd[16], uint8_
         resplen = - 1;
 
         // If sense key is not set by callback, default to Logical Unit Not Ready, Cause Not Reportable
-        if ( _mscd_itf.sense_key == 0 ) tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x04, 0x00);
+        if ( p_msc->sense_key == 0 ) tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x04, 0x00);
       }
     break;
 
@@ -498,7 +651,7 @@ static int32_t proc_builtin_scsi(uint8_t lun, uint8_t const scsi_cmd[16], uint8_
           resplen = - 1;
 
           // If sense key is not set by callback, default to Logical Unit Not Ready, Cause Not Reportable
-          if ( _mscd_itf.sense_key == 0 ) tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x04, 0x00);
+          if ( p_msc->sense_key == 0 ) tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x04, 0x00);
         }
       }
     break;
@@ -519,7 +672,7 @@ static int32_t proc_builtin_scsi(uint8_t lun, uint8_t const scsi_cmd[16], uint8_
         resplen = -1;
 
         // If sense key is not set by callback, default to Logical Unit Not Ready, Cause Not Reportable
-        if ( _mscd_itf.sense_key == 0 ) tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x04, 0x00);
+        if ( p_msc->sense_key == 0 ) tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x04, 0x00);
       }else
       {
         scsi_read_capacity10_resp_t read_capa10;
@@ -555,7 +708,7 @@ static int32_t proc_builtin_scsi(uint8_t lun, uint8_t const scsi_cmd[16], uint8_
         resplen = -1;
 
         // If sense key is not set by callback, default to Logical Unit Not Ready, Cause Not Reportable
-        if ( _mscd_itf.sense_key == 0 ) tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x04, 0x00);
+        if ( p_msc->sense_key == 0 ) tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x04, 0x00);
       }else
       {
         read_fmt_capa.block_num = tu_htonl(block_count);
@@ -600,9 +753,11 @@ static int32_t proc_builtin_scsi(uint8_t lun, uint8_t const scsi_cmd[16], uint8_
       };
 
       bool writable = true;
-      if (tud_msc_is_writable_cb) {
-          writable = tud_msc_is_writable_cb(lun);
+      if ( tud_msc_is_writable_cb )
+      {
+        writable = tud_msc_is_writable_cb(lun);
       }
+
       mode_resp.write_protected = !writable;
 
       resplen = sizeof(mode_resp);
@@ -620,9 +775,9 @@ static int32_t proc_builtin_scsi(uint8_t lun, uint8_t const scsi_cmd[16], uint8_
 
       sense_rsp.add_sense_len = sizeof(scsi_sense_fixed_resp_t) - 8;
 
-      sense_rsp.sense_key           = _mscd_itf.sense_key;
-      sense_rsp.add_sense_code      = _mscd_itf.add_sense_code;
-      sense_rsp.add_sense_qualifier = _mscd_itf.add_sense_qualifier;
+      sense_rsp.sense_key           = p_msc->sense_key;
+      sense_rsp.add_sense_code      = p_msc->add_sense_code;
+      sense_rsp.add_sense_qualifier = p_msc->add_sense_qualifier;
 
       resplen = sizeof(sense_rsp);
       memcpy(buffer, &sense_rsp, resplen);
@@ -641,13 +796,9 @@ static int32_t proc_builtin_scsi(uint8_t lun, uint8_t const scsi_cmd[16], uint8_
 static void proc_read10_cmd(uint8_t rhport, mscd_interface_t* p_msc)
 {
   msc_cbw_t const * p_cbw = &p_msc->cbw;
-  msc_csw_t       * p_csw = &p_msc->csw;
 
-  uint16_t const block_cnt = rdwr10_get_blockcount(p_cbw->command);
-  TU_ASSERT(block_cnt, ); // prevent div by zero
-
-  uint16_t const block_sz = p_cbw->total_bytes / block_cnt;
-  TU_ASSERT(block_sz, ); // prevent div by zero
+  // block size already verified not zero
+  uint16_t const block_sz = rdwr10_get_blocksize(p_cbw);
 
   // Adjust lba with transferred bytes
   uint32_t const lba = rdwr10_get_lba(p_cbw->command) + (p_msc->xferred_len / block_sz);
@@ -656,16 +807,18 @@ static void proc_read10_cmd(uint8_t rhport, mscd_interface_t* p_msc)
   int32_t nbytes = (int32_t) tu_min32(sizeof(_mscd_buf), p_cbw->total_bytes-p_msc->xferred_len);
 
   // Application can consume smaller bytes
-  nbytes = tud_msc_read10_cb(p_cbw->lun, lba, p_msc->xferred_len % block_sz, _mscd_buf, (uint32_t) nbytes);
+  uint32_t const offset = p_msc->xferred_len % block_sz;
+  nbytes = tud_msc_read10_cb(p_cbw->lun, lba, offset, _mscd_buf, (uint32_t) nbytes);
 
   if ( nbytes < 0 )
   {
-    // negative means error -> pipe is stalled & status in CSW set to failed
-    p_csw->data_residue = p_cbw->total_bytes - p_msc->xferred_len;
-    p_csw->status       = MSC_CSW_STATUS_FAILED;
+    // negative means error -> endpoint is stalled & status in CSW set to failed
+    TU_LOG(MSC_DEBUG, "  tud_msc_read10_cb() return -1\r\n");
+
+    // Sense = Flash not ready for access
+    tud_msc_set_sense(p_cbw->lun, SCSI_SENSE_MEDIUM_ERROR, 0x33, 0x00);
 
-    tud_msc_set_sense(p_cbw->lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00); // Sense = Invalid Command Operation
-    usbd_edpt_stall(rhport, p_msc->ep_in);
+    fail_scsi_op(rhport, p_msc, MSC_CSW_STATUS_FAILED);
   }
   else if ( nbytes == 0 )
   {
@@ -682,16 +835,18 @@ static void proc_write10_cmd(uint8_t rhport, mscd_interface_t* p_msc)
 {
   msc_cbw_t const * p_cbw = &p_msc->cbw;
   bool writable = true;
-  if (tud_msc_is_writable_cb) {
+
+  if ( tud_msc_is_writable_cb )
+  {
     writable = tud_msc_is_writable_cb(p_cbw->lun);
   }
-  if (!writable) {
-    msc_csw_t* p_csw = &p_msc->csw;
-    p_csw->data_residue = p_cbw->total_bytes;
-    p_csw->status       = MSC_CSW_STATUS_FAILED;
 
-    tud_msc_set_sense(p_cbw->lun, SCSI_SENSE_DATA_PROTECT, 0x27, 0x00); // Sense = Write protected
-    usbd_edpt_stall(rhport, p_msc->ep_out);
+  if ( !writable )
+  {
+    // Not writable, complete this SCSI op with error
+    // Sense = Write protected
+    tud_msc_set_sense(p_cbw->lun, SCSI_SENSE_DATA_PROTECT, 0x27, 0x00);
+    fail_scsi_op(rhport, p_msc, MSC_CSW_STATUS_FAILED);
     return;
   }
 
@@ -702,4 +857,63 @@ static void proc_write10_cmd(uint8_t rhport, mscd_interface_t* p_msc)
   TU_ASSERT( usbd_edpt_xfer(rhport, p_msc->ep_out, _mscd_buf, nbytes), );
 }
 
+// process new data arrived from WRITE10
+static void proc_write10_new_data(uint8_t rhport, mscd_interface_t* p_msc, uint32_t xferred_bytes)
+{
+  msc_cbw_t const * p_cbw = &p_msc->cbw;
+
+  // block size already verified not zero
+  uint16_t const block_sz = rdwr10_get_blocksize(p_cbw);
+
+  // Adjust lba with transferred bytes
+  uint32_t const lba = rdwr10_get_lba(p_cbw->command) + (p_msc->xferred_len / block_sz);
+
+  // Invoke callback to consume new data
+  uint32_t const offset = p_msc->xferred_len % block_sz;
+  int32_t nbytes = tud_msc_write10_cb(p_cbw->lun, lba, offset, _mscd_buf, xferred_bytes);
+
+  if ( nbytes < 0 )
+  {
+    // negative means error -> failed this scsi op
+    TU_LOG(MSC_DEBUG, "  tud_msc_write10_cb() return -1\r\n");
+
+    // update actual byte before failed
+    p_msc->xferred_len += xferred_bytes;
+
+    // Sense = Flash not ready for access
+    tud_msc_set_sense(p_cbw->lun, SCSI_SENSE_MEDIUM_ERROR, 0x33, 0x00);
+
+    fail_scsi_op(rhport, p_msc, MSC_CSW_STATUS_FAILED);
+  }else
+  {
+    // Application consume less than what we got (including zero)
+    if ( (uint32_t) nbytes < xferred_bytes )
+    {
+      if ( nbytes > 0 )
+      {
+        p_msc->xferred_len += nbytes;
+        memmove(_mscd_buf, _mscd_buf+nbytes, xferred_bytes-nbytes);
+      }
+
+      // simulate an transfer complete with adjusted parameters --> callback will be invoked with adjusted parameter
+      dcd_event_xfer_complete(rhport, p_msc->ep_out, xferred_bytes-nbytes, XFER_RESULT_SUCCESS, false);
+    }
+    else
+    {
+      // Application consume all bytes in our buffer
+      p_msc->xferred_len += xferred_bytes;
+
+      if ( p_msc->xferred_len >= p_msc->total_len )
+      {
+        // Data Stage is complete
+        p_msc->stage = MSC_STAGE_STATUS;
+      }else
+      {
+        // prepare to receive more data from host
+        proc_write10_cmd(rhport, p_msc);
+      }
+    }
+  }
+}
+
 #endif

+ 1 - 1
src/class/msc/msc_device.h

@@ -140,7 +140,7 @@ TU_ATTR_WEAK void tud_msc_write10_complete_cb(uint8_t lun);
 // Invoked when command in tud_msc_scsi_cb is complete
 TU_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. TODO remove
+// Invoked to check if device is writable as part of SCSI WRITE10
 TU_ATTR_WEAK bool tud_msc_is_writable_cb(uint8_t lun);
 
 //--------------------------------------------------------------------+

+ 6 - 1
src/common/tusb_common.h

@@ -369,12 +369,17 @@ typedef struct
 
 static inline const char* tu_lookup_find(tu_lookup_table_t const* p_table, uint32_t key)
 {
+  static char not_found[10];
+
   for(uint16_t i=0; i<p_table->count; i++)
   {
     if (p_table->items[i].key == key) return p_table->items[i].data;
   }
 
-  return NULL;
+  // not found return the key value in hex
+  sprintf(not_found, "0x%08lX", key);
+
+  return not_found;
 }
 
 #endif // CFG_TUSB_DEBUG

+ 5 - 0
src/device/dcd.h

@@ -137,6 +137,11 @@ void dcd_edpt0_status_complete(uint8_t rhport, tusb_control_request_t const * re
 // Configure endpoint's registers according to descriptor
 bool dcd_edpt_open            (uint8_t rhport, tusb_desc_endpoint_t const * desc_ep);
 
+// Close all non-control endpoints, cancel all pending transfers if any.
+// Invoked when switching from a non-zero Configuration by SET_CONFIGURE therefore
+// required for multiple configuration support.
+void dcd_edpt_close_all       (uint8_t rhport);
+
 // Close an endpoint.
 // Since it is weak, caller must TU_ASSERT this function's existence before calling it.
 void dcd_edpt_close           (uint8_t rhport, uint8_t ep_addr) TU_ATTR_WEAK;

+ 56 - 25
src/device/usbd.c

@@ -38,7 +38,7 @@
 //--------------------------------------------------------------------+
 
 // Debug level of USBD
-#define USBD_DBG_LVL   2
+#define USBD_DBG   2
 
 #ifndef CFG_TUD_TASK_QUEUE_SZ
   #define CFG_TUD_TASK_QUEUE_SZ   16
@@ -432,19 +432,22 @@ bool tud_init (uint8_t rhport)
   return true;
 }
 
-static void usbd_reset(uint8_t rhport)
+static void configuration_reset(uint8_t rhport)
 {
-  tu_varclr(&_usbd_dev);
+  for ( uint8_t i = 0; i < TOTAL_DRIVER_COUNT; i++ )
+  {
+    get_driver(i)->reset(rhport);
+  }
 
+  tu_varclr(&_usbd_dev);
   memset(_usbd_dev.itf2drv, DRVID_INVALID, sizeof(_usbd_dev.itf2drv)); // invalid mapping
   memset(_usbd_dev.ep2drv , DRVID_INVALID, sizeof(_usbd_dev.ep2drv )); // invalid mapping
+}
 
+static void usbd_reset(uint8_t rhport)
+{
+  configuration_reset(rhport);
   usbd_control_reset();
-
-  for ( uint8_t i = 0; i < TOTAL_DRIVER_COUNT; i++ )
-  {
-    get_driver(i)->reset(rhport);
-  }
 }
 
 bool tud_task_event_ready(void)
@@ -686,9 +689,29 @@ static bool process_control_request(uint8_t rhport, tusb_control_request_t const
         {
           uint8_t const cfg_num = (uint8_t) p_request->wValue;
 
-          if ( !_usbd_dev.cfg_num && cfg_num ) TU_ASSERT( process_set_config(rhport, cfg_num) );
-          _usbd_dev.cfg_num = cfg_num;
+          // Only process if new configure is different
+          if (_usbd_dev.cfg_num != cfg_num)
+          {
+            if ( _usbd_dev.cfg_num )
+            {
+              // already configured: need to clear all endpoints and driver first
+              TU_LOG(USBD_DBG, "  Clear current Configuration (%u) before switching\r\n", _usbd_dev.cfg_num);
 
+              // close all non-control endpoints, cancel all pending transfers if any
+              dcd_edpt_close_all(rhport);
+
+              // close all drivers and current configured state except bus speed
+              uint8_t const speed = _usbd_dev.speed;
+              configuration_reset(rhport);
+
+              _usbd_dev.speed = speed; // restore speed
+            }
+
+            // switch to new configuration if not zero
+            if ( cfg_num ) TU_ASSERT( process_set_config(rhport, cfg_num) );
+          }
+
+          _usbd_dev.cfg_num = cfg_num;
           tud_control_status(rhport, p_request);
         }
         break;
@@ -701,7 +724,7 @@ static bool process_control_request(uint8_t rhport, tusb_control_request_t const
           // Only support remote wakeup for device feature
           TU_VERIFY(TUSB_REQ_FEATURE_REMOTE_WAKEUP == p_request->wValue);
 
-          TU_LOG(USBD_DBG_LVL, "    Enable Remote Wakeup\r\n");
+          TU_LOG(USBD_DBG, "    Enable Remote Wakeup\r\n");
 
           // Host may enable remote wake up before suspending especially HID device
           _usbd_dev.remote_wakeup_en = true;
@@ -712,7 +735,7 @@ static bool process_control_request(uint8_t rhport, tusb_control_request_t const
           // Only support remote wakeup for device feature
           TU_VERIFY(TUSB_REQ_FEATURE_REMOTE_WAKEUP == p_request->wValue);
 
-          TU_LOG(USBD_DBG_LVL, "    Disable Remote Wakeup\r\n");
+          TU_LOG(USBD_DBG, "    Disable Remote Wakeup\r\n");
 
           // Host may disable remote wake up after resuming
           _usbd_dev.remote_wakeup_en = false;
@@ -751,16 +774,24 @@ static bool process_control_request(uint8_t rhport, tusb_control_request_t const
         // driver doesn't use alternate settings or implement this
         TU_VERIFY(TUSB_REQ_TYPE_STANDARD == p_request->bmRequestType_bit.type);
 
-        if (TUSB_REQ_GET_INTERFACE == p_request->bRequest)
+        switch(p_request->bRequest)
         {
-          uint8_t alternate = 0;
-          tud_control_xfer(rhport, p_request, &alternate, 1);
-        }else if (TUSB_REQ_SET_INTERFACE == p_request->bRequest)
-        {
-          tud_control_status(rhport, p_request);
-        } else
-        {
-          return false;
+          case TUSB_REQ_GET_INTERFACE:
+          case TUSB_REQ_SET_INTERFACE:
+            // Clear complete callback if driver set since it can also stall the request.
+            usbd_control_set_complete_callback(NULL);
+
+            if (TUSB_REQ_GET_INTERFACE == p_request->bRequest)
+            {
+              uint8_t alternate = 0;
+              tud_control_xfer(rhport, p_request, &alternate, 1);
+            }else
+            {
+              tud_control_status(rhport, p_request);
+            }
+          break;
+
+          default: return false;
         }
       }
     }
@@ -843,7 +874,8 @@ static bool process_control_request(uint8_t rhport, tusb_control_request_t const
 // This function parse configuration descriptor & open drivers accordingly
 static bool process_set_config(uint8_t rhport, uint8_t cfg_num)
 {
-  tusb_desc_configuration_t const * desc_cfg = (tusb_desc_configuration_t const *) tud_descriptor_configuration_cb(cfg_num-1); // index is cfg_num-1
+  // index is cfg_num-1
+  tusb_desc_configuration_t const * desc_cfg = (tusb_desc_configuration_t const *) tud_descriptor_configuration_cb(cfg_num-1);
   TU_ASSERT(desc_cfg != NULL && desc_cfg->bDescriptorType == TUSB_DESC_CONFIGURATION);
 
   // Parse configuration descriptor
@@ -1301,7 +1333,7 @@ bool usbd_edpt_busy(uint8_t rhport, uint8_t ep_addr)
 
 void usbd_edpt_stall(uint8_t rhport, uint8_t ep_addr)
 {
-  TU_LOG(USBD_DBG_LVL, "    Stall EP %02X", ep_addr);
+  TU_LOG(USBD_DBG, "    Stall EP %02X\r\n", ep_addr);
 
   uint8_t const epnum = tu_edpt_number(ep_addr);
   uint8_t const dir   = tu_edpt_dir(ep_addr);
@@ -1313,12 +1345,11 @@ void usbd_edpt_stall(uint8_t rhport, uint8_t ep_addr)
 
 void usbd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr)
 {
-  TU_LOG(USBD_DBG_LVL, "    Clear Stall EP %02X", ep_addr);
+  TU_LOG(USBD_DBG, "    Clear Stall EP %02X\r\n", ep_addr);
 
   uint8_t const epnum = tu_edpt_number(ep_addr);
   uint8_t const dir   = tu_edpt_dir(ep_addr);
 
-
   dcd_edpt_clear_stall(rhport, ep_addr);
   _usbd_dev.ep_status[epnum][dir].stalled = false;
   _usbd_dev.ep_status[epnum][dir].busy = false;

+ 6 - 0
src/portable/dialog/da146xx/dcd_da146xx.c

@@ -847,6 +847,12 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt)
   return true;
 }
 
+void dcd_edpt_close_all (uint8_t rhport)
+{
+  (void) rhport;
+  // TODO implement dcd_edpt_close_all()
+}
+
 void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr)
 {
   uint8_t const epnum = tu_edpt_number(ep_addr);

+ 6 - 0
src/portable/espressif/esp32sx/dcd_esp32sx.c

@@ -312,6 +312,12 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const *desc_edpt)
   return true;
 }
 
+void dcd_edpt_close_all(uint8_t rhport)
+{
+  (void) rhport;
+  // TODO implement dcd_edpt_close_all()
+}
+
 bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t total_bytes)
 {
   (void)rhport;

+ 6 - 0
src/portable/microchip/samd/dcd_samd.c

@@ -240,6 +240,12 @@ bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt)
   return true;
 }
 
+void dcd_edpt_close_all (uint8_t rhport)
+{
+  (void) rhport;
+  // TODO implement dcd_edpt_close_all()
+}
+
 bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes)
 {
   (void) rhport;

+ 6 - 0
src/portable/microchip/samg/dcd_samg.c

@@ -269,6 +269,12 @@ bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * ep_desc)
   return true;
 }
 
+void dcd_edpt_close_all (uint8_t rhport)
+{
+  (void) rhport;
+  // TODO implement dcd_edpt_close_all()
+}
+
 // Submit a transfer, When complete dcd_event_xfer_complete() is invoked to notify the stack
 bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes)
 {

+ 6 - 0
src/portable/microchip/samx7x/dcd_samx7x.c

@@ -544,6 +544,12 @@ bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * ep_desc)
   }
 }
 
+void dcd_edpt_close_all (uint8_t rhport)
+{
+  (void) rhport;
+  // TODO implement dcd_edpt_close_all()
+}
+
 void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr)
 {
   (void) rhport;

+ 6 - 0
src/portable/mindmotion/mm32/dcd_mm32f327x_otg.c

@@ -339,6 +339,12 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * ep_desc)
   return true;
 }
 
+void dcd_edpt_close_all (uint8_t rhport)
+{
+  (void) rhport;
+  // TODO implement dcd_edpt_close_all()
+}
+
 void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr)
 {
   (void) rhport;

+ 66 - 11
src/portable/nordic/nrf5x/dcd_nrf5x.c

@@ -206,7 +206,8 @@ static void xact_out_dma(uint8_t epnum)
   }
   else
   {
-    xact_len = (uint8_t)NRF_USBD->SIZE.EPOUT[epnum];
+    // limit xact len to remaining length
+    xact_len = tu_min16((uint16_t) NRF_USBD->SIZE.EPOUT[epnum], xfer->total_len - xfer->actual_len);
 
     // Trigger DMA move data from Endpoint -> SRAM
     NRF_USBD->EPOUT[epnum].PTR = (uint32_t) xfer->buffer;
@@ -306,8 +307,9 @@ bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt)
 {
   (void) rhport;
 
-  uint8_t const epnum = tu_edpt_number(desc_edpt->bEndpointAddress);
-  uint8_t const dir   = tu_edpt_dir(desc_edpt->bEndpointAddress);
+  uint8_t const ep_addr = desc_edpt->bEndpointAddress;
+  uint8_t const epnum   = tu_edpt_number(ep_addr);
+  uint8_t const dir     = tu_edpt_dir(ep_addr);
 
   _dcd.xfer[epnum][dir].mps = desc_edpt->wMaxPacketSize.size;
 
@@ -359,11 +361,48 @@ bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt)
       NRF_USBD->EPINEN  |= USBD_EPINEN_ISOIN_Msk;
     }
   }
+
+  // clear stall and reset DataToggle
+  NRF_USBD->EPSTALL = (USBD_EPSTALL_STALL_UnStall << USBD_EPSTALL_STALL_Pos) | ep_addr;
+  NRF_USBD->DTOGGLE = (USBD_DTOGGLE_VALUE_Data0 << USBD_DTOGGLE_VALUE_Pos) | ep_addr;
+
   __ISB(); __DSB();
 
   return true;
 }
 
+void dcd_edpt_close_all (uint8_t rhport)
+{
+  // disable interrupt to prevent race condition
+  dcd_int_disable(rhport);
+
+  // disable all non-control (bulk + interrupt) endpoints
+  for ( uint8_t ep = 1; ep < EP_CBI_COUNT; ep++ )
+  {
+    NRF_USBD->INTENCLR = TU_BIT(USBD_INTEN_ENDEPOUT0_Pos + ep) | TU_BIT(USBD_INTEN_ENDEPIN0_Pos + ep);
+
+    NRF_USBD->TASKS_STARTEPIN[ep] = 0;
+    NRF_USBD->TASKS_STARTEPOUT[ep] = 0;
+
+    tu_memclr(_dcd.xfer[ep], 2*sizeof(xfer_td_t));
+  }
+
+  // disable both ISO
+  NRF_USBD->INTENCLR = USBD_INTENCLR_SOF_Msk | USBD_INTENCLR_ENDISOOUT_Msk | USBD_INTENCLR_ENDISOIN_Msk;
+  NRF_USBD->ISOSPLIT = USBD_ISOSPLIT_SPLIT_OneDir;
+
+  NRF_USBD->TASKS_STARTISOIN  = 0;
+  NRF_USBD->TASKS_STARTISOOUT = 0;
+
+  tu_memclr(_dcd.xfer[EP_ISO_NUM], 2*sizeof(xfer_td_t));
+
+  // de-activate all non-control
+  NRF_USBD->EPOUTEN = 1UL;
+  NRF_USBD->EPINEN = 1UL;
+
+  dcd_int_enable(rhport);
+}
+
 void dcd_edpt_close (uint8_t rhport, uint8_t ep_addr)
 {
   (void) rhport;
@@ -442,10 +481,9 @@ bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t
     {
       if ( xfer->data_received )
       {
-        // Data may already be received previously
-        xfer->data_received = false;
-
+        // Data is already received previously
         // start DMA to copy to SRAM
+        xfer->data_received = false;
         xact_out_dma(epnum);
       }
       else
@@ -467,7 +505,11 @@ bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t
 void dcd_edpt_stall (uint8_t rhport, uint8_t ep_addr)
 {
   (void) rhport;
+
   uint8_t const epnum = tu_edpt_number(ep_addr);
+  uint8_t const dir   = tu_edpt_dir(ep_addr);
+
+  xfer_td_t* xfer = get_td(epnum, dir);
 
   if ( epnum == 0 )
   {
@@ -475,6 +517,15 @@ void dcd_edpt_stall (uint8_t rhport, uint8_t ep_addr)
   }else if (epnum != EP_ISO_NUM)
   {
     NRF_USBD->EPSTALL = (USBD_EPSTALL_STALL_Stall << USBD_EPSTALL_STALL_Pos) | ep_addr;
+
+    // Note: nRF can auto ACK packet OUT before get stalled.
+    // There maybe data in endpoint fifo already, we need to pull it out
+    if ( (dir == TUSB_DIR_OUT) && xfer->data_received )
+    {
+      TU_LOG_LOCATION();
+      xfer->data_received = false;
+      xact_out_dma(epnum);
+    }
   }
 
   __ISB(); __DSB();
@@ -488,14 +539,16 @@ void dcd_edpt_clear_stall (uint8_t rhport, uint8_t ep_addr)
 
   if ( epnum != 0 && epnum != EP_ISO_NUM )
   {
-    // clear stall
-    NRF_USBD->EPSTALL = (USBD_EPSTALL_STALL_UnStall << USBD_EPSTALL_STALL_Pos) | ep_addr;
-
     // reset data toggle to DATA0
+    // First write this register with VALUE=Nop to select the endpoint, then either read it to get the status from
+    // VALUE, or write it again with VALUE=Data0 or Data1
+    NRF_USBD->DTOGGLE = ep_addr;
     NRF_USBD->DTOGGLE = (USBD_DTOGGLE_VALUE_Data0 << USBD_DTOGGLE_VALUE_Pos) | ep_addr;
 
+    // clear stall
+    NRF_USBD->EPSTALL = (USBD_EPSTALL_STALL_UnStall << USBD_EPSTALL_STALL_Pos) | ep_addr;
+
     // Write any value to SIZE register will allow nRF to ACK/accept data
-    // Drop any pending data
     if (dir == TUSB_DIR_OUT) NRF_USBD->SIZE.EPOUT[epnum] = 0;
 
     __ISB(); __DSB();
@@ -508,7 +561,9 @@ void dcd_edpt_clear_stall (uint8_t rhport, uint8_t ep_addr)
 void bus_reset(void)
 {
   // 6.35.6 USB controller automatically disabled all endpoints (except control)
-  // i.e EPOUTEN and EPINEN and reset USBADDR to 0
+  NRF_USBD->EPOUTEN = 1UL;
+  NRF_USBD->EPINEN = 1UL;
+
   for(int i=0; i<8; i++)
   {
     NRF_USBD->TASKS_STARTEPIN[i] = 0;

+ 6 - 0
src/portable/nuvoton/nuc120/dcd_nuc120.c

@@ -273,6 +273,12 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc)
   return true;
 }
 
+void dcd_edpt_close_all (uint8_t rhport)
+{
+  (void) rhport;
+  // TODO implement dcd_edpt_close_all()
+}
+
 bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t total_bytes)
 {
   (void) rhport;

+ 6 - 0
src/portable/nuvoton/nuc121/dcd_nuc121.c

@@ -289,6 +289,12 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc)
   return true;
 }
 
+void dcd_edpt_close_all (uint8_t rhport)
+{
+  (void) rhport;
+  // TODO implement dcd_edpt_close_all()
+}
+
 bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t total_bytes)
 {
   (void) rhport;

+ 6 - 0
src/portable/nuvoton/nuc505/dcd_nuc505.c

@@ -353,6 +353,12 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc)
   return true;
 }
 
+void dcd_edpt_close_all (uint8_t rhport)
+{
+  (void) rhport;
+  // TODO implement dcd_edpt_close_all()
+}
+
 bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t total_bytes)
 {
   (void) rhport;

+ 6 - 0
src/portable/nxp/khci/dcd_khci.c

@@ -347,6 +347,12 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * ep_desc)
   return true;
 }
 
+void dcd_edpt_close_all (uint8_t rhport)
+{
+  (void) rhport;
+  // TODO implement dcd_edpt_close_all()
+}
+
 void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr)
 {
   (void) rhport;

+ 6 - 0
src/portable/nxp/lpc17_40/dcd_lpc17_40.c

@@ -326,6 +326,12 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc)
   return true;
 }
 
+void dcd_edpt_close_all (uint8_t rhport)
+{
+  (void) rhport;
+  // TODO implement dcd_edpt_close_all()
+}
+
 void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr)
 {
   (void) rhport;

+ 6 - 0
src/portable/nxp/lpc_ip3511/dcd_lpc_ip3511.c

@@ -323,6 +323,12 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc)
   return true;
 }
 
+void dcd_edpt_close_all (uint8_t rhport)
+{
+  (void) rhport;
+  // TODO implement dcd_edpt_close_all()
+}
+
 static void prepare_setup_packet(uint8_t rhport)
 {
   if (_dcd_controller[rhport].max_speed == TUSB_SPEED_FULL )

+ 6 - 0
src/portable/nxp/transdimension/dcd_transdimension.c

@@ -366,6 +366,12 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc)
   return true;
 }
 
+void dcd_edpt_close_all (uint8_t rhport)
+{
+  (void) rhport;
+  // TODO implement dcd_edpt_close_all()
+}
+
 bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes)
 {
   dcd_registers_t* dcd_reg = _dcd_controller[rhport].regs;

+ 6 - 0
src/portable/raspberrypi/rp2040/dcd_rp2040.c

@@ -428,6 +428,12 @@ bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt)
     return true;
 }
 
+void dcd_edpt_close_all (uint8_t rhport)
+{
+  (void) rhport;
+  // TODO implement dcd_edpt_close_all()
+}
+
 bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes)
 {
     assert(rhport == 0);

+ 6 - 0
src/portable/renesas/usba/dcd_usba.c

@@ -733,6 +733,12 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * ep_desc)
   return true;
 }
 
+void dcd_edpt_close_all (uint8_t rhport)
+{
+  (void) rhport;
+  // TODO implement dcd_edpt_close_all()
+}
+
 void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr)
 {
   (void)rhport;

+ 6 - 0
src/portable/silabs/efm32/dcd_efm32.c

@@ -434,6 +434,12 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc)
   return true;
 }
 
+void dcd_edpt_close_all (uint8_t rhport)
+{
+  (void) rhport;
+  // TODO implement dcd_edpt_close_all()
+}
+
 bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes)
 {
   (void)rhport;

+ 6 - 0
src/portable/sony/cxd56/dcd_cxd56.c

@@ -312,6 +312,12 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const *p_endpoint_desc)
   return true;
 }
 
+void dcd_edpt_close_all (uint8_t rhport)
+{
+  (void) rhport;
+  // TODO implement dcd_edpt_close_all()
+}
+
 bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t total_bytes)
 {
   (void) rhport;

+ 6 - 0
src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c

@@ -800,6 +800,12 @@ bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc
   return true;
 }
 
+void dcd_edpt_close_all (uint8_t rhport)
+{
+  (void) rhport;
+  // TODO implement dcd_edpt_close_all()
+}
+
 /**
  * Close an endpoint.
  * 

+ 6 - 0
src/portable/st/synopsys/dcd_synopsys.c

@@ -670,6 +670,12 @@ bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt)
   return true;
 }
 
+void dcd_edpt_close_all (uint8_t rhport)
+{
+  (void) rhport;
+  // TODO implement dcd_edpt_close_all()
+}
+
 bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes)
 {
   uint8_t const epnum = tu_edpt_number(ep_addr);

+ 5 - 0
src/portable/template/dcd_template.c

@@ -94,6 +94,11 @@ bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * ep_desc)
   return false;
 }
 
+void dcd_edpt_close_all (uint8_t rhport)
+{
+  (void) rhport;
+}
+
 // Submit a transfer, When complete dcd_event_xfer_complete() is invoked to notify the stack
 bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes)
 {

+ 6 - 0
src/portable/ti/msp430x5xx/dcd_msp430x5xx.c

@@ -298,6 +298,12 @@ bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt)
   return true;
 }
 
+void dcd_edpt_close_all (uint8_t rhport)
+{
+  (void) rhport;
+  // TODO implement dcd_edpt_close_all()
+}
+
 bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes)
 {
   (void) rhport;

+ 6 - 0
src/portable/valentyusb/eptri/dcd_eptri.c

@@ -429,6 +429,12 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc)
   return true;
 }
 
+void dcd_edpt_close_all (uint8_t rhport)
+{
+  (void) rhport;
+  // TODO implement dcd_edpt_close_all()
+}
+
 void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr)
 {
   (void) rhport;