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

Merge pull request #51 from hathach/develop

Add suspend, resume and remote wakeup support
hathach 7 лет назад
Родитель
Сommit
0848c462b3
44 измененных файлов с 639 добавлено и 330 удалено
  1. 3 3
      README.md
  2. 3 0
      docs/porting.md
  3. 5 0
      examples/device/cdc_msc_hid/ses/samd21/samd21.emProject
  4. 5 0
      examples/device/cdc_msc_hid/ses/samd51/samd51.emProject
  5. 50 8
      examples/device/cdc_msc_hid/src/main.c
  6. 1 1
      examples/device/cdc_msc_hid_freertos/src/main.c
  7. 15 22
      hw/bsp/board.h
  8. 1 1
      hw/bsp/ea4088qs/board_ea4088qs.c
  9. 1 1
      hw/bsp/ea4357/board_ea4357.c
  10. 1 1
      hw/bsp/lpcxpresso11u68/board_lpcxpresso11u68.c
  11. 1 1
      hw/bsp/lpcxpresso1347/board_lpcxpresso1347.c
  12. 1 1
      hw/bsp/lpcxpresso1769/board_lpcxpresso1769.c
  13. 1 1
      hw/bsp/mcb1800/board_mcb1800.c
  14. 2 1
      hw/bsp/metro_m0_express/board.mk
  15. 10 4
      hw/bsp/metro_m0_express/board_metro_m0_express.c
  16. 2 1
      hw/bsp/metro_m4_express/board.mk
  17. 11 1
      hw/bsp/metro_m4_express/board_metro_m4_express.c
  18. 6 16
      hw/bsp/pca10056/board_pca10056.c
  19. 6 0
      hw/bsp/stm32f303disc/board_stm32f303disc.c
  20. 6 0
      hw/bsp/stm32f407g_disc1/board_stm32f407g_disc1.c
  21. 1 1
      src/class/cdc/cdc_device.c
  22. 0 2
      src/class/cdc/cdc_device.h
  23. 8 1
      src/class/hid/hid_device.c
  24. 0 5
      src/class/msc/msc_device.c
  25. 0 3
      src/class/msc/msc_device.h
  26. 8 2
      src/common/tusb_types.h
  27. 11 5
      src/device/dcd.h
  28. 221 123
      src/device/usbd.c
  29. 28 15
      src/device/usbd.h
  30. 2 2
      src/device/usbd_auto_desc.c
  31. 1 0
      src/device/usbd_control.c
  32. 0 1
      src/osal/osal.h
  33. 0 5
      src/osal/osal_freertos.h
  34. 0 5
      src/osal/osal_mynewt.h
  35. 0 7
      src/osal/osal_none.h
  36. 62 25
      src/portable/microchip/samd21/dcd_samd21.c
  37. 61 18
      src/portable/microchip/samd51/dcd_samd51.c
  38. 52 11
      src/portable/nordic/nrf5x/dcd_nrf5x.c
  39. 2 2
      src/portable/nordic/nrf5x/hal_nrf5x.c
  40. 22 19
      src/portable/nxp/lpc11_13_15/dcd_lpc11_13_15.c
  41. 7 5
      src/portable/nxp/lpc17_40/dcd_lpc17_40.c
  42. 7 4
      src/portable/nxp/lpc18_43/dcd_lpc18_43.c
  43. 9 3
      src/portable/st/stm32f3/dcd_stm32f3.c
  44. 6 3
      src/portable/st/stm32f4/dcd_stm32f4.c

+ 3 - 3
README.md

@@ -47,15 +47,15 @@ Currently the following OS are supported with tinyusb out of the box with a simp
 The stack supports the following MCUs
 
 - **Nordic:** nRF52840
-- **NXP:** LPC11Uxx, LPC13xx, LPC175x_6x, LPC177x_8x, LPC40xx, LPC43xx
-- **MicroChip:** SAMD21, SAMD51 (device only)
+- **NXP:** LPC11Uxx, LPC13xx, LPC175x_6x, LPC177x_8x, LPC18xx, LPC40xx, LPC43xx
+- **MicroChip:** SAMD21, SAMD51
 - **ST:** STM32F4
 
 [Here is the list of supported Boards](docs/boards.md)
 
 ## Compiler & IDE
 
-The stack is developed with GCC compiler, and should be compilable with others. However, it requires C99 to build with. Folder `examples` provide Makefile and Segger Embedded Studio build support.
+The stack is developed with GCC compiler, and should be compilable with others. Folder `examples` provide Makefile and Segger Embedded Studio build support.
 
 ## Getting Started
 

+ 3 - 0
docs/porting.md

@@ -76,6 +76,9 @@ If your peripheral automatically changes address during enumeration (like the nr
 ##### dcd_set_config
 Called when the device received SET_CONFIG request, you can leave this empty if your peripheral does not require any specific action.
 
+##### dcd_remote_wakeup
+Called to remote wake up host when suspended (e.g hid keyboard)
+
 #### Special events
 You must let TinyUSB know when certain events occur so that it can continue its work. There are a few methods you can call to queue events for TinyUSB to process.
 

+ 5 - 0
examples/device/cdc_msc_hid/ses/samd21/samd21.emProject

@@ -67,6 +67,11 @@
                     <file file_name="../../../../../hw/mcu/microchip/samd/asf4/samd21/hpl/sysctrl/hpl_sysctrl.c" />
                   </folder>
                 </folder>
+                <folder Name="hal">
+                  <folder Name="src">
+                    <file file_name="../../../../../hw/mcu/microchip/samd/asf4/samd21/hal/src/hal_atomic.c" />
+                  </folder>
+                </folder>
               </folder>
             </folder>
           </folder>

+ 5 - 0
examples/device/cdc_msc_hid/ses/samd51/samd51.emProject

@@ -71,6 +71,11 @@
                     <file file_name="../../../../../hw/mcu/microchip/samd/asf4/samd51/hpl/gclk/hpl_gclk.c" />
                   </folder>
                 </folder>
+                <folder Name="hal">
+                  <folder Name="src">
+                    <file file_name="../../../../../hw/mcu/microchip/samd/asf4/samd51/hal/src/hal_atomic.c" />
+                  </folder>
+                </folder>
               </folder>
             </folder>
           </folder>

+ 50 - 8
examples/device/cdc_msc_hid/src/main.c

@@ -34,6 +34,14 @@
 //--------------------------------------------------------------------+
 // MACRO CONSTANT TYPEDEF PROTYPES
 //--------------------------------------------------------------------+
+
+/* Blink pattern
+ * - 250 ms  : device not mounted
+ * - 1000 ms : device mounted
+ * - 2500 ms : device is suspended
+ */
+static uint32_t blink_interval_ms = 250;
+
 void led_blinking_task(void);
 
 extern void virtual_com_task(void);
@@ -127,8 +135,16 @@ void usb_hid_task(void)
   if ( board_millis() < start_ms + interval_ms) return; // not enough time
   start_ms += interval_ms;
 
-  uint32_t const btn = board_buttons();
+  uint32_t const btn = board_button_read();
+
+  if ( tud_suspended() && btn )
+  {
+    // Wake up host if we are in suspend mode
+    // and REMOTE_WAKEUP feature is enabled by host
+    tud_remote_wakeup();
+  }
 
+#if 0
   /*------------- Keyboard -------------*/
   if ( tud_hid_keyboard_ready() )
   {
@@ -148,8 +164,9 @@ void usb_hid_task(void)
       tud_hid_keyboard_keycode(0, NULL);
     }
   }
+#endif
 
-
+#if 0
   /*------------- Mouse -------------*/
   if ( tud_hid_mouse_ready() )
   {
@@ -160,33 +177,60 @@ void usb_hid_task(void)
     if ( btn & 0x04 ) tud_hid_mouse_move(  0   , -DELTA); // up
     if ( btn & 0x08 ) tud_hid_mouse_move(  0   ,  DELTA); // down
   }
+#endif
 }
 
 uint16_t tud_hid_generic_get_report_cb(uint8_t report_id, hid_report_type_t report_type, uint8_t* buffer, uint16_t reqlen)
 {
   // TODO not Implemented
+  (void) report_id;
+  (void) report_type;
+  (void) buffer;
+  (void) reqlen;
+
   return 0;
 }
 
 void tud_hid_generic_set_report_cb(uint8_t report_id, hid_report_type_t report_type, uint8_t const* buffer, uint16_t bufsize)
 {
   // TODO not Implemented
+  (void) report_id;
+  (void) report_type;
+  (void) buffer;
+  (void) bufsize;
 }
+
 #endif
 
 //--------------------------------------------------------------------+
-// tinyusb callbacks
+// Device callbacks
 //--------------------------------------------------------------------+
 
 // Invoked when device is mounted
 void tud_mount_cb(void)
 {
-
+  blink_interval_ms = 1000;
 }
 
 // Invoked when device is unmounted
 void tud_umount_cb(void)
 {
+  blink_interval_ms = 250;
+}
+
+// Invoked when usb bus is suspended
+// remote_wakeup_en : if host allow us  to perform remote wakeup
+// Within 7ms, device must draw an average of current less than 2.5 mA from bus
+void tud_suspend_cb(bool remote_wakeup_en)
+{
+  (void) remote_wakeup_en;
+  blink_interval_ms = 2500;
+}
+
+// Invoked when usb bus is resumed
+void tud_resume_cb(void)
+{
+  blink_interval_ms = 1000;
 }
 
 //--------------------------------------------------------------------+
@@ -194,14 +238,12 @@ void tud_umount_cb(void)
 //--------------------------------------------------------------------+
 void led_blinking_task(void)
 {
-  const uint32_t interval_ms = 1000;
   static uint32_t start_ms = 0;
-
   static bool led_state = false;
 
   // Blink every 1000 ms
-  if ( board_millis() < start_ms + interval_ms) return; // not enough time
-  start_ms += interval_ms;
+  if ( board_millis() < start_ms + blink_interval_ms) return; // not enough time
+  start_ms += blink_interval_ms;
 
   board_led_control(led_state);
   led_state = 1 - led_state; // toggle

+ 1 - 1
examples/device/cdc_msc_hid_freertos/src/main.c

@@ -158,7 +158,7 @@ void usb_hid_task(void* params)
   if ( board_millis() < start_ms + interval_ms) return; // not enough time
   start_ms += interval_ms;
 
-  uint32_t const btn = board_buttons();
+  uint32_t const btn = board_button_read();
 
   /*------------- Keyboard -------------*/
   if ( tud_hid_keyboard_ready() )

+ 15 - 22
hw/bsp/board.h

@@ -46,6 +46,7 @@
 
 //--------------------------------------------------------------------+
 // Board Porting API
+// For simplicity, only one LED and one Button are used
 //--------------------------------------------------------------------+
 
 // Initialize on-board peripherals : led, button, uart and USB
@@ -54,9 +55,9 @@ void board_init(void);
 // Turn LED on or off
 void board_led_control(bool state);
 
-// Get the current state of buttons on the board
-// \return Bitmask where a '1' means active (pressed), a '0' means inactive.
-uint32_t board_buttons(void);
+// Get the current state of button
+// a '1' means active (pressed), a '0' means inactive.
+uint32_t board_button_read(void);
 
 // Get characters from UART
 int board_uart_read(uint8_t* buf, int len);
@@ -65,28 +66,20 @@ int board_uart_read(uint8_t* buf, int len);
 int board_uart_write(void const * buf, int len);
 
 #if CFG_TUSB_OS == OPT_OS_NONE
-
-// Get current milliseconds, must be implemented in board.c when no OS is used
-uint32_t board_millis(void);
-
+  // Get current milliseconds, must be implemented when no RTOS is used
+  uint32_t board_millis(void);
 #elif CFG_TUSB_OS == OPT_OS_FREERTOS
-
-static inline uint32_t board_millis(void)
-{
-  return ( ( ((uint64_t) xTaskGetTickCount()) * 1000) / configTICK_RATE_HZ );
-}
-
+  static inline uint32_t board_millis(void)
+  {
+    return ( ( ((uint64_t) xTaskGetTickCount()) * 1000) / configTICK_RATE_HZ );
+  }
 #elif CFG_TUSB_OS == OPT_OS_MYNEWT
-
-static inline uint32_t board_millis(void)
-{
-  return os_time_ticks_to_ms32( os_time_get() );
-}
-
+  static inline uint32_t board_millis(void)
+  {
+    return os_time_ticks_to_ms32( os_time_get() );
+  }
 #elif
-
-#error "Need to implement board_millis() for this OS"
-
+  #error "Need to implement board_millis() for this OS"
 #endif
 
 //--------------------------------------------------------------------+

+ 1 - 1
hw/bsp/ea4088qs/board_ea4088qs.c

@@ -127,7 +127,7 @@ static bool button_read(uint8_t id)
 }
 #endif
 
-uint32_t board_buttons(void)
+uint32_t board_button_read(void)
 {
   uint32_t result = 0;
 

+ 1 - 1
hw/bsp/ea4357/board_ea4357.c

@@ -269,7 +269,7 @@ static bool button_read(uint8_t id)
 }
 #endif
 
-uint32_t board_buttons(void)
+uint32_t board_button_read(void)
 {
   uint32_t result = 0;
 

+ 1 - 1
hw/bsp/lpcxpresso11u68/board_lpcxpresso11u68.c

@@ -121,7 +121,7 @@ void board_led_control(bool state)
 //--------------------------------------------------------------------+
 // Buttons
 //--------------------------------------------------------------------+
-uint32_t board_buttons(void)
+uint32_t board_button_read(void)
 {
 //  for(uint8_t i=0; i<BOARD_BUTTON_COUNT; i++) GPIOGetPinValue(buttons[i].port, buttons[i].pin);
 //  return GPIOGetPinValue(buttons[0].port, buttons[0].pin) ? 0 : 1; // button is active low

+ 1 - 1
hw/bsp/lpcxpresso1347/board_lpcxpresso1347.c

@@ -144,7 +144,7 @@ static bool button_read(uint8_t id)
 }
 #endif
 
-uint32_t board_buttons(void)
+uint32_t board_button_read(void)
 {
   uint32_t result = 0;
 

+ 1 - 1
hw/bsp/lpcxpresso1769/board_lpcxpresso1769.c

@@ -180,7 +180,7 @@ static bool button_read(uint8_t id)
 }
 #endif
 
-uint32_t board_buttons(void)
+uint32_t board_button_read(void)
 {
   uint32_t result = 0;
 

+ 1 - 1
hw/bsp/mcb1800/board_mcb1800.c

@@ -193,7 +193,7 @@ static bool button_read(uint8_t id)
 }
 */
 
-uint32_t board_buttons(void)
+uint32_t board_button_read(void)
 {
   uint32_t result = 0;
 

+ 2 - 1
hw/bsp/metro_m0_express/board.mk

@@ -19,7 +19,8 @@ SRC_C += \
 	hw/mcu/microchip/samd/asf4/samd21/gcc/system_samd21.c \
 	hw/mcu/microchip/samd/asf4/samd21/hpl/gclk/hpl_gclk.c \
 	hw/mcu/microchip/samd/asf4/samd21/hpl/pm/hpl_pm.c \
-	hw/mcu/microchip/samd/asf4/samd21/hpl/sysctrl/hpl_sysctrl.c
+	hw/mcu/microchip/samd/asf4/samd21/hpl/sysctrl/hpl_sysctrl.c \
+	hw/mcu/microchip/samd/asf4/samd21/hal/src/hal_atomic.c
 
 INC += \
 	-I$(TOP)/hw/mcu/microchip/samd/asf4/samd21/ \

+ 10 - 4
hw/bsp/metro_m0_express/board_metro_m0_express.c

@@ -39,6 +39,7 @@
 // MACRO TYPEDEF CONSTANT ENUM DECLARATION
 //--------------------------------------------------------------------+
 #define LED_PIN      17
+#define BUTTON_PIN   14 // pin D2
 
 /* Referenced GCLKs, should be initialized firstly */
 #define _GCLK_INIT_1ST (1 << 0 | 1 << 1)
@@ -63,6 +64,10 @@ void board_init(void)
   gpio_set_pin_direction(LED_PIN, GPIO_DIRECTION_OUT);
   gpio_set_pin_level(LED_PIN, 0);
 
+  // Button init
+  gpio_set_pin_direction(BUTTON_PIN, GPIO_DIRECTION_IN);
+  gpio_set_pin_pull_mode(BUTTON_PIN, GPIO_PULL_UP);
+
 #if CFG_TUSB_OS  == OPT_OS_NONE
   // 1ms tick timer (samd SystemCoreClock may not correct)
   SysTick_Config(CONF_CPU_FREQUENCY / 1000);
@@ -101,10 +106,12 @@ void board_led_control(bool state)
   gpio_set_pin_level(LED_PIN, state);
 }
 
+uint32_t board_button_read(void)
+{
+  // button is active low
+  return gpio_get_pin_level(BUTTON_PIN) ? 0 : 1;
+}
 
-/*------------------------------------------------------------------*/
-/* TUSB HAL MILLISECOND
- *------------------------------------------------------------------*/
 #if CFG_TUSB_OS  == OPT_OS_NONE
 volatile uint32_t system_ticks = 0;
 
@@ -118,5 +125,4 @@ uint32_t board_millis(void)
   return system_ticks;
 }
 
-
 #endif

+ 2 - 1
hw/bsp/metro_m4_express/board.mk

@@ -21,7 +21,8 @@ SRC_C += \
 	hw/mcu/microchip/samd/asf4/samd51/hpl/gclk/hpl_gclk.c \
 	hw/mcu/microchip/samd/asf4/samd51/hpl/mclk/hpl_mclk.c \
 	hw/mcu/microchip/samd/asf4/samd51/hpl/osc32kctrl/hpl_osc32kctrl.c \
-	hw/mcu/microchip/samd/asf4/samd51/hpl/oscctrl/hpl_oscctrl.c
+	hw/mcu/microchip/samd/asf4/samd51/hpl/oscctrl/hpl_oscctrl.c \
+	hw/mcu/microchip/samd/asf4/samd51/hal/src/hal_atomic.c
 
 INC += \
 	-I$(TOP)/hw/mcu/microchip/samd/asf4/samd51/ \

+ 11 - 1
hw/bsp/metro_m4_express/board_metro_m4_express.c

@@ -35,7 +35,8 @@
 //--------------------------------------------------------------------+
 // MACRO TYPEDEF CONSTANT ENUM DECLARATION
 //--------------------------------------------------------------------+
-#define LED_PIN         16
+#define LED_PIN      16
+#define BUTTON_PIN   (32 + 17)  // pin D2
 
 /* Referenced GCLKs, should be initialized firstly */
 #define _GCLK_INIT_1ST 0xFFFFFFFF
@@ -61,6 +62,10 @@ void board_init(void)
   gpio_set_pin_direction(LED_PIN, GPIO_DIRECTION_OUT);
   gpio_set_pin_level(LED_PIN, 0);
 
+  // Button init
+  gpio_set_pin_direction(BUTTON_PIN, GPIO_DIRECTION_IN);
+  gpio_set_pin_pull_mode(BUTTON_PIN, GPIO_PULL_UP);
+
 #if CFG_TUSB_OS  == OPT_OS_NONE
   // 1ms tick timer (samd SystemCoreClock may not correct)
   SysTick_Config(CONF_CPU_FREQUENCY / 1000);
@@ -90,6 +95,11 @@ void board_led_control(bool state)
   gpio_set_pin_level(LED_PIN, state);
 }
 
+uint32_t board_button_read(void)
+{
+  // button is active low
+  return gpio_get_pin_level(BUTTON_PIN) ? 0 : 1;
+}
 
 /*------------------------------------------------------------------*/
 /* TUSB HAL MILLISECOND

+ 6 - 16
hw/bsp/pca10056/board_pca10056.c

@@ -42,10 +42,7 @@
 #define LED_PIN         13
 #define LED_STATE_ON    0
 
-uint8_t _button_pins[] = { 11, 12, 24, 25 };
-
-#define BOARD_BUTTON_COUNT  sizeof(_button_pins)
-
+#define BUTTON_PIN      11
 
 /*------------------------------------------------------------------*/
 /* TUSB HAL MILLISECOND
@@ -79,12 +76,12 @@ void board_init(void)
   NRF_CLOCK->LFCLKSRC = (uint32_t)((CLOCK_LFCLKSRC_SRC_Xtal << CLOCK_LFCLKSRC_SRC_Pos) & CLOCK_LFCLKSRC_SRC_Msk);
   NRF_CLOCK->TASKS_LFCLKSTART = 1UL;
 
-  // LEDs
+  // LED
   nrf_gpio_cfg_output(LED_PIN);
   board_led_control(false);
 
   // Button
-  for(uint8_t i=0; i<BOARD_BUTTON_COUNT; i++) nrf_gpio_cfg_input(_button_pins[i], NRF_GPIO_PIN_PULLUP);
+  nrf_gpio_cfg_input(BUTTON_PIN, NRF_GPIO_PIN_PULLUP);
 
 #if CFG_TUSB_OS == OPT_OS_NONE
   // 1ms tick timer
@@ -134,17 +131,10 @@ void board_led_control(bool state)
   nrf_gpio_pin_write(LED_PIN, state ? LED_STATE_ON : (1-LED_STATE_ON));
 }
 
-uint32_t board_buttons(void)
+uint32_t board_button_read(void)
 {
-  uint32_t ret = 0;
-
-  for(uint8_t i=0; i<BOARD_BUTTON_COUNT; i++)
-  {
-    // button is active LOW
-    ret |= ( nrf_gpio_pin_read(_button_pins[i]) ? 0 : (1 << i));
-  }
-
-  return ret;
+  // button is active LOW
+  return (nrf_gpio_pin_read(BUTTON_PIN) ? 0 : 1);
 }
 
 int board_uart_read(uint8_t* buf, int len)

+ 6 - 0
hw/bsp/stm32f303disc/board_stm32f303disc.c

@@ -97,6 +97,12 @@ void board_led_control(bool state)
   HAL_GPIO_WritePin(GPIOE, GPIO_PIN_9, state);
 }
 
+uint32_t board_button_read(void)
+{
+  // TODO implement
+  return 0;
+}
+
 /*------------------------------------------------------------------*/
 /* TUSB HAL MILLISECOND
  *------------------------------------------------------------------*/

+ 6 - 0
hw/bsp/stm32f407g_disc1/board_stm32f407g_disc1.c

@@ -91,6 +91,12 @@ void board_led_control(bool state)
   }
 }
 
+uint32_t board_button_read(void)
+{
+  // TODO implement
+  return 0;
+}
+
 
 /*------------------------------------------------------------------*/
 /* TUSB HAL MILLISECOND

+ 1 - 1
src/class/cdc/cdc_device.c

@@ -99,7 +99,7 @@ static void _prep_out_transaction (uint8_t itf)
 bool tud_cdc_n_connected(uint8_t itf)
 {
   // DTR (bit 0) active  is considered as connected
-  return TU_BIT_TEST(_cdcd_itf[itf].line_state, 0);
+  return tud_ready() && TU_BIT_TEST(_cdcd_itf[itf].line_state, 0);
 }
 
 uint8_t tud_cdc_n_get_line_state (uint8_t itf)

+ 0 - 2
src/class/cdc/cdc_device.h

@@ -38,7 +38,6 @@
 #define CFG_TUD_CDC_EPSIZE 64
 #endif
 
-
 #ifdef __cplusplus
  extern "C" {
 #endif
@@ -97,7 +96,6 @@ ATTR_WEAK void tud_cdc_line_coding_cb(uint8_t itf, cdc_line_coding_t const* p_li
 
 //--------------------------------------------------------------------+
 // INTERNAL USBD-CLASS DRIVER API
-
 //--------------------------------------------------------------------+
 void cdcd_init               (void);
 bool cdcd_open               (uint8_t rhport, tusb_desc_interface_t const * p_interface_desc, uint16_t *p_length);

+ 8 - 1
src/class/hid/hid_device.c

@@ -107,7 +107,7 @@ static inline hidd_interface_t* get_interface_by_itfnum(uint8_t itf_num)
 //--------------------------------------------------------------------+
 bool tud_hid_generic_ready(void)
 {
-  return (_hidd_itf[ITF_IDX_GENERIC].ep_in != 0) && !dcd_edpt_busy(TUD_OPT_RHPORT, _hidd_itf[ITF_IDX_GENERIC].ep_in);
+  return tud_ready() && (_hidd_itf[ITF_IDX_GENERIC].ep_in != 0) && !dcd_edpt_busy(TUD_OPT_RHPORT, _hidd_itf[ITF_IDX_GENERIC].ep_in);
 }
 
 bool tud_hid_generic_report(uint8_t report_id, void const* report, uint8_t len)
@@ -265,6 +265,7 @@ void hidd_init(void)
 
 void hidd_reset(uint8_t rhport)
 {
+  (void) rhport;
   tu_memclr(_hidd_itf, sizeof(_hidd_itf));
 
   #if CFG_TUD_HID_KEYBOARD
@@ -447,6 +448,7 @@ bool hidd_control_request(uint8_t rhport, tusb_control_request_t const * p_reque
 // return false to stall control endpoint (e.g Host send non-sense DATA)
 bool hidd_control_request_complete(uint8_t rhport, tusb_control_request_t const * p_request)
 {
+  (void) rhport;
   hidd_interface_t* p_hid = get_interface_by_itfnum( (uint8_t) p_request->wIndex );
   TU_ASSERT(p_hid);
 
@@ -469,6 +471,11 @@ bool hidd_control_request_complete(uint8_t rhport, tusb_control_request_t const
 bool hidd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes)
 {
   // nothing to do
+  (void) rhport;
+  (void) ep_addr;
+  (void) event;
+  (void) xferred_bytes;
+
   return true;
 }
 

+ 0 - 5
src/class/msc/msc_device.c

@@ -102,11 +102,6 @@ static inline uint16_t rdwr10_get_blockcount(uint8_t const command[])
 //--------------------------------------------------------------------+
 // APPLICATION API
 //--------------------------------------------------------------------+
-bool tud_msc_ready(void)
-{
-  return ( _mscd_itf.ep_in != 0 ) && ( _mscd_itf.ep_out != 0 ) ;
-}
-
 bool tud_msc_set_sense(uint8_t lun, uint8_t sense_key, uint8_t add_sense_code, uint8_t add_sense_qualifier)
 {
   (void) lun;

+ 0 - 3
src/class/msc/msc_device.h

@@ -68,9 +68,6 @@ TU_VERIFY_STATIC(CFG_TUD_MSC_BUFSIZE < UINT16_MAX, "Size is not correct");
  * \defgroup MSC_Device Device
  *  @{ */
 
-
-// Check if MSC interface is ready to use
-bool tud_msc_ready(void);
 bool tud_msc_set_sense(uint8_t lun, uint8_t sense_key, uint8_t add_sense_code, uint8_t add_sense_qualifier);
 
 //--------------------------------------------------------------------+

+ 8 - 2
src/common/tusb_types.h

@@ -103,6 +103,13 @@ typedef enum
   TUSB_REQ_SYNCH_FRAME         ///< 12
 }tusb_request_code_t;
 
+typedef enum
+{
+  TUSB_REQ_FEATURE_EDPT_HALT = 0,
+  TUSB_REQ_FEATURE_REMOTE_WAKEUP = 1,
+  TUSB_REQ_FEATURE_TEST_MODE = 2
+}tusb_request_feature_selector_t;
+
 typedef enum
 {
   TUSB_REQ_TYPE_STANDARD = 0,
@@ -159,8 +166,7 @@ typedef enum
 
 enum {
   TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP = TU_BIT(5),
-  TUSB_DESC_CONFIG_ATT_SELF_POWER    = TU_BIT(6),
-  TUSB_DESC_CONFIG_ATT_BUS_POWER     = TU_BIT(7)
+  TUSB_DESC_CONFIG_ATT_SELF_POWERED  = TU_BIT(6),
 };
 
 #define TUSB_DESC_CONFIG_POWER_MA(x)  ((x)/2)

+ 11 - 5
src/device/dcd.h

@@ -42,13 +42,14 @@ typedef enum
   DCD_EVENT_BUS_RESET = 1,
   DCD_EVENT_UNPLUGGED,
   DCD_EVENT_SOF,
-  DCD_EVENT_SUSPENDED,
+  DCD_EVENT_SUSPEND,
   DCD_EVENT_RESUME,
 
   DCD_EVENT_SETUP_RECEIVED,
   DCD_EVENT_XFER_COMPLETE,
 
-  USBD_EVT_FUNC_CALL
+  // Not an DCD event, just a convenient way to defer ISR function
+  USBD_EVENT_FUNC_CALL
 } dcd_eventid_t;
 
 typedef struct ATTR_ALIGNED(4)
@@ -67,7 +68,7 @@ typedef struct ATTR_ALIGNED(4)
       uint32_t len;
     }xfer_complete;
 
-    // USBD_EVT_FUNC_CALL
+    // USBD_EVENT_FUNC_CALL
     struct {
       void (*func) (void*);
       void* param;
@@ -80,7 +81,9 @@ TU_VERIFY_STATIC(sizeof(dcd_event_t) <= 12, "size is not correct");
 /*------------------------------------------------------------------*/
 /* Device API
  *------------------------------------------------------------------*/
-bool dcd_init       (uint8_t rhport);
+
+// Initialize controller to device mode
+void dcd_init       (uint8_t rhport);
 
 // Enable device interrupt
 void dcd_int_enable (uint8_t rhport);
@@ -91,9 +94,12 @@ void dcd_int_disable(uint8_t rhport);
 // Receive Set Address request, mcu port must also include status IN response
 void dcd_set_address(uint8_t rhport, uint8_t dev_addr);
 
-// Receive Set Config request
+// Receive Set Configure request
 void dcd_set_config (uint8_t rhport, uint8_t config_num);
 
+// Wake up host
+void dcd_remote_wakeup(uint8_t rhport);
+
 /*------------------------------------------------------------------*/
 /* Endpoint API
  *  - open        : Configure endpoint's registers

+ 221 - 123
src/device/usbd.c

@@ -40,13 +40,22 @@
 // Device Data
 //--------------------------------------------------------------------+
 typedef struct {
-  uint8_t config_num;
+  struct ATTR_PACKED
+  {
+      volatile uint8_t connected    : 1;
+      volatile uint8_t configured   : 1;
+      volatile uint8_t suspended    : 1;
 
-  uint8_t itf2drv[16];      // map interface number to driver (0xff is invalid)
-  uint8_t ep2drv[8][2];     // map endpoint to driver ( 0xff is invalid )
+      uint8_t remote_wakeup_en      : 1; // enable/disable by host
+      uint8_t remote_wakeup_support : 1; // configuration descriptor's attribute
+      uint8_t self_powered          : 1; // configuration descriptor's attribute
+  };
 
-  uint8_t ep_busy_mask[2];  // bit mask for busy endpoint
+//  uint8_t ep_busy_mask[2];  // bit mask for busy endpoint
   uint8_t ep_stall_mask[2]; // bit mask for stalled endpoint
+
+  uint8_t itf2drv[16];      // map interface number to driver (0xff is invalid)
+  uint8_t ep2drv[8][2];     // map endpoint to driver ( 0xff is invalid )
 }usbd_device_t;
 
 static usbd_device_t _usbd_dev = { 0 };
@@ -166,11 +175,24 @@ bool usbd_control_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t event,
 void usbd_control_set_complete_callback( bool (*fp) (uint8_t, tusb_control_request_t const * ) );
 
 //--------------------------------------------------------------------+
-// APPLICATION API
+// Application API
 //--------------------------------------------------------------------+
 bool tud_mounted(void)
 {
-  return _usbd_dev.config_num > 0;
+  return _usbd_dev.configured;
+}
+
+bool tud_suspended(void)
+{
+  return _usbd_dev.suspended;
+}
+
+bool tud_remote_wakeup(void)
+{
+  // only wake up host if this feature is supported and enabled and we are suspended
+  TU_VERIFY (_usbd_dev.suspended && _usbd_dev.remote_wakeup_support && _usbd_dev.remote_wakeup_en );
+  dcd_remote_wakeup(TUD_OPT_RHPORT);
+  return true;
 }
 
 //--------------------------------------------------------------------+
@@ -178,6 +200,8 @@ bool tud_mounted(void)
 //--------------------------------------------------------------------+
 bool usbd_init (void)
 {
+  tu_varclr(&_usbd_dev);
+
   // Init device queue & task
   _usbd_q = osal_queue_create(&_usbd_qdef);
   TU_ASSERT(_usbd_q != NULL);
@@ -186,7 +210,7 @@ bool usbd_init (void)
   for (uint8_t i = 0; i < USBD_CLASS_DRIVER_COUNT; i++) usbd_class_drivers[i].init();
 
   // Init device controller driver
-  TU_ASSERT(dcd_init(TUD_OPT_RHPORT));
+  dcd_init(TUD_OPT_RHPORT);
   dcd_int_enable(TUD_OPT_RHPORT);
 
   return true;
@@ -195,6 +219,7 @@ bool usbd_init (void)
 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
 
@@ -239,7 +264,22 @@ void tud_task (void)
 
     switch ( event.event_id )
     {
+      case DCD_EVENT_BUS_RESET:
+        usbd_reset(event.rhport);
+      break;
+
+      case DCD_EVENT_UNPLUGGED:
+        usbd_reset(event.rhport);
+
+        // invoke callback
+        if (tud_umount_cb) tud_umount_cb();
+      break;
+
       case DCD_EVENT_SETUP_RECEIVED:
+        // Mark as connected after receiving 1st setup packet.
+        // But it is easier to set it every time instead of wasting time to check then set
+        _usbd_dev.connected = 1;
+
         // Process control request
         if ( !process_control_request(event.rhport, &event.setup_received) )
         {
@@ -250,38 +290,33 @@ void tud_task (void)
       break;
 
       case DCD_EVENT_XFER_COMPLETE:
-      {
-        // Invoke the class callback associated with the endpoint address
-        uint8_t const ep_addr = event.xfer_complete.ep_addr;
-
-        if ( 0 == tu_edpt_number(ep_addr) )
+        // Only handle xfer callback in ready state
+        // if (_usbd_dev.connected && !_usbd_dev.suspended)
         {
-          // control transfer DATA stage callback
-          usbd_control_xfer_cb(event.rhport, ep_addr, event.xfer_complete.result, event.xfer_complete.len);
-        }
-        else
-        {
-          uint8_t const drv_id = _usbd_dev.ep2drv[tu_edpt_number(ep_addr)][tu_edpt_dir(ep_addr)];
-          TU_ASSERT(drv_id < USBD_CLASS_DRIVER_COUNT,);
+          // Invoke the class callback associated with the endpoint address
+          uint8_t const ep_addr = event.xfer_complete.ep_addr;
+
+          if ( 0 == tu_edpt_number(ep_addr) )
+          {
+            // control transfer DATA stage callback
+            usbd_control_xfer_cb(event.rhport, ep_addr, event.xfer_complete.result, event.xfer_complete.len);
+          }
+          else
+          {
+            uint8_t const drv_id = _usbd_dev.ep2drv[tu_edpt_number(ep_addr)][tu_edpt_dir(ep_addr)];
+            TU_ASSERT(drv_id < USBD_CLASS_DRIVER_COUNT,);
 
-          usbd_class_drivers[drv_id].xfer_cb(event.rhport, ep_addr, 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);
+          }
         }
-      }
       break;
 
-      case DCD_EVENT_BUS_RESET:
-        usbd_reset(event.rhport);
-        // TODO remove since if task is too slow, we could clear the event of the new attached
-        osal_queue_reset(_usbd_q);
+      case DCD_EVENT_SUSPEND:
+        if (tud_suspend_cb) tud_suspend_cb(_usbd_dev.remote_wakeup_en);
       break;
 
-      case DCD_EVENT_UNPLUGGED:
-        usbd_reset(event.rhport);
-        // TODO remove since if task is too slow, we could clear the event of the new attached
-        osal_queue_reset(_usbd_q);
-
-        // invoke callback
-        if (tud_umount_cb) tud_umount_cb();
+      case DCD_EVENT_RESUME:
+        if (tud_resume_cb) tud_resume_cb();
       break;
 
       case DCD_EVENT_SOF:
@@ -294,7 +329,7 @@ void tud_task (void)
         }
       break;
 
-      case USBD_EVT_FUNC_CALL:
+      case USBD_EVENT_FUNC_CALL:
         if ( event.func_call.func ) event.func_call.func(event.func_call.param);
       break;
 
@@ -315,98 +350,140 @@ static bool process_control_request(uint8_t rhport, tusb_control_request_t const
 {
   usbd_control_set_complete_callback(NULL);
 
-  if ( TUSB_REQ_RCPT_DEVICE == p_request->bmRequestType_bit.recipient &&
-       TUSB_REQ_TYPE_STANDARD == p_request->bmRequestType_bit.type )
+  switch ( p_request->bmRequestType_bit.recipient )
   {
-    //------------- Standard Device Requests e.g in enumeration -------------//
-    void* data_buf = NULL;
-    uint16_t data_len = 0;
+    //------------- Device Requests e.g in enumeration -------------//
+    case TUSB_REQ_RCPT_DEVICE:
+      if ( TUSB_REQ_TYPE_STANDARD != p_request->bmRequestType_bit.type )
+      {
+        // Non standard request is not supported
+        TU_BREAKPOINT();
+        return false;
+      }
 
-    switch ( p_request->bRequest )
-    {
-      case TUSB_REQ_SET_ADDRESS:
-        // DCD must include zero-length status response since depending on mcu,
-        // status could be sent either before or after changing device address
-        dcd_set_address(rhport, (uint8_t) p_request->wValue);
-        return true; // skip the rest
-      break;
+      switch ( p_request->bRequest )
+      {
+        case TUSB_REQ_SET_ADDRESS:
+          // Depending on mcu, status phase could be sent either before or after changing device address
+          // Therefore DCD must include zero-length status response
+          dcd_set_address(rhport, (uint8_t) p_request->wValue);
+          return true; // skip status
+        break;
+
+        case TUSB_REQ_GET_CONFIGURATION:
+        {
+          uint8_t cfgnum = _usbd_dev.configured ? 1 : 0;
+          usbd_control_xfer(rhport, p_request, &cfgnum, 1);
+        }
+        break;
 
-      case TUSB_REQ_GET_CONFIGURATION:
-        data_buf = &_usbd_dev.config_num;
-        data_len = 1;
-      break;
+        case TUSB_REQ_SET_CONFIGURATION:
+        {
+          uint8_t const cfg_num = (uint8_t) p_request->wValue;
 
-      case TUSB_REQ_SET_CONFIGURATION:
-      {
-        uint8_t const config = (uint8_t) p_request->wValue;
+          dcd_set_config(rhport, cfg_num);
+          _usbd_dev.configured = cfg_num ? 1 : 0;
 
-        dcd_set_config(rhport, config);
-        _usbd_dev.config_num = config;
+          TU_ASSERT( process_set_config(rhport) );
+          usbd_control_status(rhport, p_request);
+        }
+        break;
 
-        TU_ASSERT( TUSB_ERROR_NONE == process_set_config(rhport) );
-      }
-      break;
+        case TUSB_REQ_GET_DESCRIPTOR:
+        {
+          uint16_t len = 0;
+          void* buf = (void*) get_descriptor(p_request, &len);
+          if ( buf == NULL || len == 0 ) return false;
 
-      case TUSB_REQ_GET_DESCRIPTOR:
-        data_buf = (void*) get_descriptor(p_request, &data_len);
-        if ( data_buf == NULL || data_len == 0 ) return false;
-      break;
+          usbd_control_xfer(rhport, p_request, buf, len);
+        }
+        break;
 
-      default:
-        TU_BREAKPOINT();
-      return false;
-    }
+        case TUSB_REQ_SET_FEATURE:
+          // Only support remote wakeup for device feature
+          TU_VERIFY(TUSB_REQ_FEATURE_REMOTE_WAKEUP == p_request->wValue);
+
+          // Host may enable remote wake up before suspending especially HID device
+          _usbd_dev.remote_wakeup_en = true;
+          usbd_control_status(rhport, p_request);
+        break;
+
+        case TUSB_REQ_CLEAR_FEATURE:
+          // Only support remote wakeup for device feature
+          TU_VERIFY(TUSB_REQ_FEATURE_REMOTE_WAKEUP == p_request->wValue);
+
+          // Host may disable remote wake up after resuming
+          _usbd_dev.remote_wakeup_en = false;
+          usbd_control_status(rhport, p_request);
+        break;
+
+        case TUSB_REQ_GET_STATUS:
+        {
+          // Device status bit mask
+          // - Bit 0: Self Powered
+          // - Bit 1: Remote Wakeup enabled
+          uint16_t status = (_usbd_dev.self_powered ? 1 : 0) | (_usbd_dev.remote_wakeup_en ? 2 : 0);
+          usbd_control_xfer(rhport, p_request, &status, 2);
+        }
+        break;
+
+        // Unknown/Unsupported request
+        default: TU_BREAKPOINT(); return false;
+      }
+    break;
 
-    usbd_control_xfer(rhport, p_request, data_buf, data_len);
-  }
-  else if ( TUSB_REQ_RCPT_INTERFACE == p_request->bmRequestType_bit.recipient )
-  {
     //------------- Class/Interface Specific Request -------------//
-    uint8_t const itf = tu_u16_low(p_request->wIndex);
-    uint8_t const drvid = _usbd_dev.itf2drv[ itf ];
+    case TUSB_REQ_RCPT_INTERFACE:
+    {
+      uint8_t const itf = tu_u16_low(p_request->wIndex);
+      uint8_t const drvid = _usbd_dev.itf2drv[itf];
 
-    TU_VERIFY(drvid < USBD_CLASS_DRIVER_COUNT);
+      TU_VERIFY(drvid < USBD_CLASS_DRIVER_COUNT);
 
-    usbd_control_set_complete_callback(usbd_class_drivers[drvid].control_request_complete );
+      usbd_control_set_complete_callback(usbd_class_drivers[drvid].control_request_complete );
+
+      // stall control endpoint if driver return false
+      return usbd_class_drivers[drvid].control_request(rhport, p_request);
+    }
+    break;
 
-    // control endpoint will be stalled if driver return false
-    return usbd_class_drivers[drvid].control_request(rhport, p_request);
-  }
-  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:
+    case TUSB_REQ_RCPT_ENDPOINT:
+      // Non standard request is not supported
+      TU_VERIFY( TUSB_REQ_TYPE_STANDARD == p_request->bmRequestType_bit.type );
+
+      switch ( p_request->bRequest )
       {
-        uint16_t status = usbd_edpt_stalled(rhport, tu_u16_low(p_request->wIndex)) ? 0x0001 : 0x0000;
-        usbd_control_xfer(rhport, p_request, &status, 2);
-      }
-      break;
+        case TUSB_REQ_GET_STATUS:
+        {
+          uint16_t status = usbd_edpt_stalled(rhport, tu_u16_low(p_request->wIndex)) ? 0x0001 : 0x0000;
+          usbd_control_xfer(rhport, p_request, &status, 2);
+        }
+        break;
 
-      case TUSB_REQ_CLEAR_FEATURE:
-        // only endpoint feature is halted/stalled
-        dcd_edpt_clear_stall(rhport, tu_u16_low(p_request->wIndex));
-        usbd_control_status(rhport, p_request);
-      break;
+        case TUSB_REQ_CLEAR_FEATURE:
+          if ( TUSB_REQ_FEATURE_EDPT_HALT == p_request->wValue )
+          {
+            dcd_edpt_clear_stall(rhport, tu_u16_low(p_request->wIndex));
+          }
+          usbd_control_status(rhport, p_request);
+        break;
 
-      case TUSB_REQ_SET_FEATURE:
-        // only endpoint feature is halted/stalled
-        usbd_edpt_stall(rhport, tu_u16_low(p_request->wIndex));
-        usbd_control_status(rhport, p_request);
-      break;
+        case TUSB_REQ_SET_FEATURE:
+          if ( TUSB_REQ_FEATURE_EDPT_HALT == p_request->wValue )
+          {
+            usbd_edpt_stall(rhport, tu_u16_low(p_request->wIndex));
+          }
+          usbd_control_status(rhport, p_request);
+        break;
 
-      default:
-        TU_BREAKPOINT();
-      return false;
-    }
-  }
-  else
-  {
-    //------------- Unsupported Request -------------//
-    TU_BREAKPOINT();
-    return false;
+        // Unknown/Unsupported request
+        default: TU_BREAKPOINT(); return false;
+      }
+    break;
+
+    // Unknown recipient
+    default: TU_BREAKPOINT(); return false;
   }
 
   return true;
@@ -416,13 +493,18 @@ 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 const * desc_cfg = (uint8_t const *) usbd_desc_set->config;
-  TU_ASSERT(desc_cfg != NULL);
+  tusb_desc_configuration_t const * desc_cfg = (tusb_desc_configuration_t const *) usbd_desc_set->config;
+  TU_ASSERT(desc_cfg != NULL && desc_cfg->bDescriptorType == TUSB_DESC_CONFIGURATION);
+
+  // Parse configuration descriptor
+  _usbd_dev.remote_wakeup_support = (desc_cfg->bmAttributes & TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP) ? 1 : 0;
+  _usbd_dev.self_powered = (desc_cfg->bmAttributes & TUSB_DESC_CONFIG_ATT_SELF_POWERED) ? 1 : 0;
 
-  uint8_t const * p_desc = desc_cfg + sizeof(tusb_desc_configuration_t);
-  uint16_t const cfg_len = ((tusb_desc_configuration_t*)desc_cfg)->wTotalLength;
+  // Parse interface descriptor
+  uint8_t const * p_desc   = ((uint8_t const*) desc_cfg) + sizeof(tusb_desc_configuration_t);
+  uint8_t const * desc_end = ((uint8_t const*) desc_cfg) + desc_cfg->wTotalLength;
 
-  while( p_desc < desc_cfg + cfg_len )
+  while( p_desc < desc_end )
   {
     // Each interface always starts with Interface or Association descriptor
     if ( TUSB_DESC_INTERFACE_ASSOCIATION == tu_desc_type(p_desc) )
@@ -459,7 +541,7 @@ static bool process_set_config(uint8_t rhport)
   // invoke callback
   if (tud_mount_cb) tud_mount_cb();
 
-  return TUSB_ERROR_NONE;
+  return true;
 }
 
 // Helper marking endpoint of interface belongs to class driver
@@ -515,9 +597,8 @@ static void const* get_descriptor(tusb_control_request_t const * p_request, uint
       }else
       {
         // out of range
-        /* The 0xEE index string is a Microsoft USB extension.
-         * It can be used to tell Windows what driver it should use for the device !!!
-         */
+        // The 0xEE index string is a Microsoft USB extension.
+        // It can be used to tell Windows what driver it should use for the device !!!
         return NULL;
       }
     break;
@@ -543,7 +624,13 @@ void dcd_event_handler(dcd_event_t const * event, bool in_isr)
   switch (event->event_id)
   {
     case DCD_EVENT_BUS_RESET:
+      osal_queue_send(_usbd_q, event, in_isr);
+    break;
+
     case DCD_EVENT_UNPLUGGED:
+      _usbd_dev.connected = 0;
+      _usbd_dev.configured = 0;
+      _usbd_dev.suspended = 0;
       osal_queue_send(_usbd_q, event, in_isr);
     break;
 
@@ -551,12 +638,23 @@ void dcd_event_handler(dcd_event_t const * event, bool in_isr)
       // nothing to do now
     break;
 
-    case DCD_EVENT_SUSPENDED:
-      // TODO support suspended
+    case DCD_EVENT_SUSPEND:
+      // NOTE: When plugging/unplugging device, the D+/D- state are unstable and can accidentally meet the
+      // SUSPEND condition ( Idle for 3ms ). Some MCUs such as samd don't distinguish suspend vs disconnect as well.
+      // We will skip handling SUSPEND/RESUME event if not currently connected
+      if ( _usbd_dev.connected )
+      {
+        _usbd_dev.suspended = 1;
+        osal_queue_send(_usbd_q, event, in_isr);
+      }
     break;
 
     case DCD_EVENT_RESUME:
-      // TODO support resume
+      if ( _usbd_dev.connected )
+      {
+        _usbd_dev.suspended = 0;
+        osal_queue_send(_usbd_q, event, in_isr);
+      }
     break;
 
     case DCD_EVENT_SETUP_RECEIVED:
@@ -565,14 +663,14 @@ void dcd_event_handler(dcd_event_t const * event, bool in_isr)
 
     case DCD_EVENT_XFER_COMPLETE:
       // skip zero-length control status complete event, should dcd notifies us.
-      if ( 0 == tu_edpt_number(event->xfer_complete.ep_addr) && event->xfer_complete.len == 0) break;
+      if ( (0 == tu_edpt_number(event->xfer_complete.ep_addr)) && (event->xfer_complete.len == 0) ) break;
 
       osal_queue_send(_usbd_q, event, in_isr);
       TU_ASSERT(event->xfer_complete.result == XFER_RESULT_SUCCESS,);
     break;
 
-    // Not an DCD event, just a convenient way to defer ISR function should we need
-    case USBD_EVT_FUNC_CALL:
+    // Not an DCD event, just a convenient way to defer ISR function should we need to
+    case USBD_EVENT_FUNC_CALL:
       osal_queue_send(_usbd_q, event, in_isr);
     break;
 
@@ -642,7 +740,7 @@ void usbd_defer_func(osal_task_func_t func, void* param, bool in_isr)
   dcd_event_t event =
   {
       .rhport   = 0,
-      .event_id = USBD_EVT_FUNC_CALL,
+      .event_id = USBD_EVENT_FUNC_CALL,
   };
 
   event.func_call.func  = func;

+ 28 - 15
src/device/usbd.h

@@ -34,16 +34,9 @@
  extern "C" {
 #endif
 
-//--------------------------------------------------------------------+
-// INCLUDE
-//--------------------------------------------------------------------+
-#include <common/tusb_common.h>
+#include "common/tusb_common.h"
 #include "device/dcd.h"
 
-//--------------------------------------------------------------------+
-// MACRO CONSTANT TYPEDEF
-//--------------------------------------------------------------------+
-
 /// \brief Descriptor pointer collector to all the needed.
 typedef struct {
   void const * device;            ///< pointer to device descriptor \ref tusb_desc_device_t
@@ -60,27 +53,47 @@ typedef struct {
 
 }tud_desc_set_t;
 
-
 // Must be defined by application
 extern tud_desc_set_t tud_desc_set;
 
 //--------------------------------------------------------------------+
-// APPLICATION API
+// Application API
 //--------------------------------------------------------------------+
-bool tud_mounted(void);
+
+// Task function should be called in main/rtos loop
 void tud_task (void);
 
+// Check if device is connected and configured
+bool tud_mounted(void);
+
+// Check if device is suspended
+bool tud_suspended(void);
+
+// Check if device is ready to transfer
+static inline bool tud_ready(void)
+{
+  return tud_mounted() && !tud_suspended();
+}
+
+// Remote wake up host, only if suspended and enabled by host
+bool tud_remote_wakeup(void);
+
 //--------------------------------------------------------------------+
-// APPLICATION CALLBACK (WEAK is optional)
+// Application Callbacks (WEAK is optional)
 //--------------------------------------------------------------------+
 
-// Callback invoked when device is mounted (configured)
+// Invoked when device is mounted (configured)
 ATTR_WEAK void tud_mount_cb(void);
 
-// Callback invoked when device is unmounted (bus reset/unplugged)
+// Invoked when device is unmounted
 ATTR_WEAK void tud_umount_cb(void);
 
-//void tud_device_suspended_cb(void);
+// Invoked when usb bus is suspended
+// Within 7ms, device must draw an average of current less than 2.5 mA from bus
+ATTR_WEAK void tud_suspend_cb(bool remote_wakeup_en);
+
+// Invoked when usb bus is resumed
+ATTR_WEAK void tud_resume_cb(void);
 
 #ifdef __cplusplus
  }

+ 2 - 2
src/device/usbd_auto_desc.c

@@ -254,7 +254,7 @@ tusb_desc_device_t const _desc_auto_device =
     .iProduct           = 0x02,
     .iSerialNumber      = 0x03,
 
-    .bNumConfigurations = 0x01 // TODO multiple configurations
+    .bNumConfigurations = 0x01
 };
 
 
@@ -342,7 +342,7 @@ desc_auto_cfg_t const _desc_auto_config_struct =
 
         .bConfigurationValue = 1,
         .iConfiguration      = 0x00,
-        .bmAttributes        = TUSB_DESC_CONFIG_ATT_BUS_POWER,
+        .bmAttributes        = TU_BIT(7) | TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP,
         .bMaxPower           = TUSB_DESC_CONFIG_POWER_MA(100)
     },
 

+ 1 - 0
src/device/usbd_control.c

@@ -114,6 +114,7 @@ bool usbd_control_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t result
 
   if ( _control_state.request.bmRequestType_bit.direction == TUSB_DIR_OUT )
   {
+    TU_VERIFY(_control_state.buffer);
     memcpy(_control_state.buffer, _usbd_ctrl_buf, xferred_bytes);
   }
 

+ 0 - 1
src/osal/osal.h

@@ -61,7 +61,6 @@ typedef void (*osal_task_func_t)( void * );
    *     osal_queue_t osal_queue_create(osal_queue_def_t* qdef)
    *     osal_queue_receive (osal_queue_t const queue_hdl, void *p_data, uint32_t msec, uint32_t *p_error)
    *     bool osal_queue_send(osal_queue_t const queue_hdl, void const * data, bool in_isr)
-   *     osal_queue_reset()
    *
    * Semaphore
    *    osal_semaphore_def_t, osal_semaphore_t

+ 0 - 5
src/osal/osal_freertos.h

@@ -125,11 +125,6 @@ static inline bool osal_queue_send(osal_queue_t const queue_hdl, void const * da
   return in_isr ? xQueueSendToBackFromISR(queue_hdl, data, NULL) : xQueueSendToBack(queue_hdl, data, OSAL_TIMEOUT_WAIT_FOREVER);
 }
 
-static inline void osal_queue_reset(osal_queue_t const queue_hdl)
-{
-  xQueueReset(queue_hdl);
-}
-
 #ifdef __cplusplus
  }
 #endif

+ 0 - 5
src/osal/osal_mynewt.h

@@ -161,11 +161,6 @@ static inline bool osal_queue_send(osal_queue_t const qhdl, void const * data, b
   return true;
 }
 
-static inline void osal_queue_reset(osal_queue_t const queue_hdl)
-{
-  // TODO implement later
-}
-
 #ifdef __cplusplus
  }
 #endif

+ 0 - 7
src/osal/osal_none.h

@@ -190,13 +190,6 @@ static inline bool osal_queue_send(osal_queue_t const qhdl, void const * data, b
   return success;
 }
 
-static inline void osal_queue_reset(osal_queue_t const qhdl)
-{
-  // tusb_hal_int_disable_all();
-  tu_fifo_clear(&qhdl->ff);
-  // tusb_hal_int_enable_all();
-}
-
 #ifdef __cplusplus
  }
 #endif

+ 62 - 25
src/portable/microchip/samd21/dcd_samd21.c

@@ -38,26 +38,27 @@ static ATTR_ALIGNED(4) UsbDeviceDescBank sram_registers[8][2];
 static ATTR_ALIGNED(4) uint8_t _setup_packet[8];
 
 // 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;
-
-    // Prepare for setup packet
-    dcd_edpt_xfer(0, 0, _setup_packet, sizeof(_setup_packet));
+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;
+
+  // Prepare for setup packet
+  dcd_edpt_xfer(0, 0, _setup_packet, sizeof(_setup_packet));
 }
 
 
 /*------------------------------------------------------------------*/
 /* Controller API
  *------------------------------------------------------------------*/
-bool dcd_init (uint8_t rhport)
+void dcd_init (uint8_t rhport)
 {
   (void) rhport;
 
@@ -79,9 +80,8 @@ bool dcd_init (uint8_t rhport)
   USB->DEVICE.CTRLA.reg = USB_CTRLA_MODE_DEVICE | USB_CTRLA_ENABLE | USB_CTRLA_RUNSTDBY;
   while (USB->DEVICE.SYNCBUSY.bit.ENABLE == 1) {}
 
+  USB->DEVICE.INTFLAG.reg |= USB->DEVICE.INTFLAG.reg; // clear pending
   USB->DEVICE.INTENSET.reg = USB_DEVICE_INTENSET_SOF | USB_DEVICE_INTENSET_EORST;
-
-  return true;
 }
 
 void dcd_int_enable(uint8_t rhport)
@@ -105,6 +105,10 @@ void dcd_set_address (uint8_t rhport, uint8_t dev_addr)
   while (USB->DEVICE.DeviceEndpoint[0].EPSTATUS.bit.BK1RDY == 1) {}
 
   USB->DEVICE.DADD.reg = USB_DEVICE_DADD_DADD(dev_addr) | USB_DEVICE_DADD_ADDEN;
+
+  // Enable SUSPEND interrupt since the bus signal D+/D- are stable now.
+  USB->DEVICE.INTFLAG.reg = USB_DEVICE_INTENCLR_SUSPEND; // clear pending
+  USB->DEVICE.INTENSET.reg = USB_DEVICE_INTENSET_SUSPEND;
 }
 
 void dcd_set_config (uint8_t rhport, uint8_t config_num)
@@ -112,6 +116,14 @@ void dcd_set_config (uint8_t rhport, uint8_t config_num)
   (void) rhport;
   (void) config_num;
   // Nothing to do
+
+}
+
+void dcd_remote_wakeup(uint8_t rhport)
+{
+  (void) rhport;
+
+  USB->DEVICE.CTRLB.bit.UPRSM = 1;
 }
 
 /*------------------------------------------------------------------*/
@@ -292,21 +304,46 @@ void maybe_transfer_complete(void) {
     }
 }
 
-void USB_Handler(void) {
-  uint32_t int_status = USB->DEVICE.INTFLAG.reg;
+void USB_Handler(void)
+{
+  uint32_t int_status = USB->DEVICE.INTFLAG.reg & USB->DEVICE.INTENSET.reg;
+  USB->DEVICE.INTFLAG.reg = int_status; // clear interrupt
 
   /*------------- Interrupt Processing -------------*/
-  if ( int_status & USB_DEVICE_INTFLAG_EORST )
+  if ( int_status & USB_DEVICE_INTFLAG_SOF )
   {
-    USB->DEVICE.INTFLAG.reg = USB_DEVICE_INTENCLR_EORST;
-    bus_reset();
-    dcd_event_bus_signal(0, DCD_EVENT_BUS_RESET, true);
+    dcd_event_bus_signal(0, DCD_EVENT_SOF, true);
   }
 
-  if ( int_status & USB_DEVICE_INTFLAG_SOF )
+  // SAMD doesn't distinguish between Suspend and Disconnect state.
+  // Both condition will cause SUSPEND interrupt triggered.
+  // To prevent being triggered when D+/D- are not stable, SUSPEND interrupt is only
+  // enabled when we received SET_ADDRESS request and cleared on Bus Reset
+  if ( int_status & USB_DEVICE_INTFLAG_SUSPEND )
   {
-    USB->DEVICE.INTFLAG.reg = USB_DEVICE_INTFLAG_SOF;
-    dcd_event_bus_signal(0, DCD_EVENT_SOF, true);
+    // Enable wakeup interrupt
+    USB->DEVICE.INTFLAG.reg = USB_DEVICE_INTFLAG_WAKEUP; // clear pending
+    USB->DEVICE.INTENSET.reg = USB_DEVICE_INTFLAG_WAKEUP;
+
+    dcd_event_bus_signal(0, DCD_EVENT_SUSPEND, true);
+  }
+
+  // Wakeup interrupt is only enabled when we got suspended.
+  // Wakeup interrupt will disable itself
+  if ( int_status & USB_DEVICE_INTFLAG_WAKEUP )
+  {
+    // disable wakeup interrupt itself
+    USB->DEVICE.INTENCLR.reg = USB_DEVICE_INTFLAG_WAKEUP;
+    dcd_event_bus_signal(0, DCD_EVENT_RESUME, true);
+  }
+
+  if ( int_status & USB_DEVICE_INTFLAG_EORST )
+  {
+    // Disable both suspend and wakeup interrupt
+    USB->DEVICE.INTENCLR.reg = USB_DEVICE_INTFLAG_WAKEUP | USB_DEVICE_INTFLAG_SUSPEND;
+
+    bus_reset();
+    dcd_event_bus_signal(0, DCD_EVENT_BUS_RESET, true);
   }
 
   // Setup packet received.

+ 61 - 18
src/portable/microchip/samd51/dcd_samd51.c

@@ -38,26 +38,27 @@ static UsbDeviceDescBank sram_registers[8][2];
 static ATTR_ALIGNED(4) uint8_t _setup_packet[8];
 
 // 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;
-
-    // Prepare for setup packet
-    dcd_edpt_xfer(0, 0, _setup_packet, sizeof(_setup_packet));
+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;
+
+  // Prepare for setup packet
+  dcd_edpt_xfer(0, 0, _setup_packet, sizeof(_setup_packet));
 }
 
 
 /*------------------------------------------------------------------*/
 /* Controller API
  *------------------------------------------------------------------*/
-bool dcd_init (uint8_t rhport)
+void dcd_init (uint8_t rhport)
 {
   (void) rhport;
 
@@ -78,9 +79,9 @@ bool dcd_init (uint8_t rhport)
   USB->DEVICE.CTRLB.reg = USB_DEVICE_CTRLB_SPDCONF_FS;
   USB->DEVICE.CTRLA.reg = USB_CTRLA_MODE_DEVICE | USB_CTRLA_ENABLE | USB_CTRLA_RUNSTDBY;
   while (USB->DEVICE.SYNCBUSY.bit.ENABLE == 1) {}
-  USB->DEVICE.INTENSET.reg = USB_DEVICE_INTENSET_SOF | USB_DEVICE_INTENSET_EORST;
 
-  return true;
+  USB->DEVICE.INTFLAG.reg |= USB->DEVICE.INTFLAG.reg; // clear pending
+  USB->DEVICE.INTENSET.reg = USB_DEVICE_INTENSET_SOF | USB_DEVICE_INTENSET_EORST;
 }
 
 void dcd_int_enable(uint8_t rhport)
@@ -110,6 +111,10 @@ void dcd_set_address (uint8_t rhport, uint8_t dev_addr)
   while (USB->DEVICE.DeviceEndpoint[0].EPSTATUS.bit.BK1RDY == 1) {}
 
   USB->DEVICE.DADD.reg = USB_DEVICE_DADD_DADD(dev_addr) | USB_DEVICE_DADD_ADDEN;
+
+  // Enable SUSPEND interrupt since the bus signal D+/D- are stable now.
+  USB->DEVICE.INTFLAG.reg = USB_DEVICE_INTENCLR_SUSPEND; // clear pending
+  USB->DEVICE.INTENSET.reg = USB_DEVICE_INTENSET_SUSPEND;
 }
 
 void dcd_set_config (uint8_t rhport, uint8_t config_num)
@@ -119,6 +124,13 @@ void dcd_set_config (uint8_t rhport, uint8_t config_num)
   // Nothing to do
 }
 
+void dcd_remote_wakeup(uint8_t rhport)
+{
+  (void) rhport;
+
+  USB->DEVICE.CTRLB.bit.UPRSM = 1;
+}
+
 /*------------------------------------------------------------------*/
 /* DCD Endpoint port
  *------------------------------------------------------------------*/
@@ -267,12 +279,42 @@ 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;
+  uint32_t int_status = USB->DEVICE.INTFLAG.reg & USB->DEVICE.INTENSET.reg;
 
   /*------------- Interrupt Processing -------------*/
+  // SAMD doesn't distinguish between Suspend and Disconnect state.
+  // Both condition will cause SUSPEND interrupt triggered.
+  // To prevent being triggered when D+/D- are not stable, SUSPEND interrupt is only
+  // enabled when we received SET_ADDRESS request and cleared on Bus Reset
+  if ( int_status & USB_DEVICE_INTFLAG_SUSPEND )
+  {
+    USB->DEVICE.INTFLAG.reg = USB_DEVICE_INTFLAG_SUSPEND;
+
+    // Enable wakeup interrupt
+    USB->DEVICE.INTFLAG.reg = USB_DEVICE_INTFLAG_WAKEUP; // clear pending
+    USB->DEVICE.INTENSET.reg = USB_DEVICE_INTFLAG_WAKEUP;
+
+    dcd_event_bus_signal(0, DCD_EVENT_SUSPEND, true);
+  }
+
+  // Wakeup interrupt is only enabled when we got suspended.
+  // Wakeup interrupt will disable itself
+  if ( int_status & USB_DEVICE_INTFLAG_WAKEUP )
+  {
+    USB->DEVICE.INTFLAG.reg = USB_DEVICE_INTFLAG_WAKEUP;
+
+    // disable wakeup interrupt itself
+    USB->DEVICE.INTENCLR.reg = USB_DEVICE_INTFLAG_WAKEUP;
+    dcd_event_bus_signal(0, DCD_EVENT_RESUME, true);
+  }
+
   if ( int_status & USB_DEVICE_INTFLAG_EORST )
   {
-    USB->DEVICE.INTFLAG.reg = USB_DEVICE_INTENCLR_EORST;
+    USB->DEVICE.INTFLAG.reg = USB_DEVICE_INTFLAG_EORST;
+
+    // Disable both suspend and wakeup interrupt
+    USB->DEVICE.INTENCLR.reg = USB_DEVICE_INTFLAG_WAKEUP | USB_DEVICE_INTFLAG_SUSPEND;
+
     bus_reset();
     dcd_event_bus_signal(0, DCD_EVENT_BUS_RESET, true);
   }
@@ -280,6 +322,7 @@ void USB_0_Handler(void) {
   // Setup packet received.
   maybe_handle_setup_packet();
 }
+
 /* USB_SOF_HSOF */
 void USB_1_Handler(void) {
     USB->DEVICE.INTFLAG.reg = USB_DEVICE_INTFLAG_SOF;

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

@@ -177,10 +177,9 @@ static void xact_in_prepare(uint8_t epnum)
 //--------------------------------------------------------------------+
 // Controller API
 //--------------------------------------------------------------------+
-bool dcd_init (uint8_t rhport)
+void dcd_init (uint8_t rhport)
 {
   (void) rhport;
-  return true;
 }
 
 void dcd_int_enable(uint8_t rhport)
@@ -200,13 +199,36 @@ void dcd_set_address (uint8_t rhport, uint8_t dev_addr)
   (void) rhport;
   (void) dev_addr;
   // Set Address is automatically update by hw controller, nothing to do
+
+  // Enable usbevent for suspend and resume detection
+  // Since the bus signal D+/D- are stable now.
+
+  // Clear current pending first
+  NRF_USBD->EVENTCAUSE |= NRF_USBD->EVENTCAUSE;
+  NRF_USBD->EVENTS_USBEVENT = 0;
+
+  NRF_USBD->INTENSET = USBD_INTEN_USBEVENT_Msk;
 }
 
 void dcd_set_config (uint8_t rhport, uint8_t config_num)
 {
   (void) rhport;
   (void) config_num;
-  // Nothing to do
+}
+
+void dcd_remote_wakeup(uint8_t rhport)
+{
+  (void) rhport;
+
+  // Bring controller out of low power mode
+  NRF_USBD->LOWPOWER = 0;
+
+  // Initiate RESUME signal
+  NRF_USBD->DPDMVALUE = USBD_DPDMVALUE_STATE_Resume;
+  NRF_USBD->TASKS_DPDMDRIVE = 1;
+
+  // TODO There is no USBEVENT Resume interrupt
+  // We may manually raise DCD_EVENT_RESUME event here
 }
 
 //--------------------------------------------------------------------+
@@ -358,19 +380,44 @@ void USBD_IRQHandler(void)
     }
   }
 
-  /*------------- Interrupt Processing -------------*/
   if ( int_status & USBD_INTEN_USBRESET_Msk )
   {
     bus_reset();
     dcd_event_bus_signal(0, DCD_EVENT_BUS_RESET, true);
   }
 
+  if ( int_status & USBD_INTEN_SOF_Msk )
+  {
+    dcd_event_bus_signal(0, DCD_EVENT_SOF, true);
+  }
+
+  if ( int_status & USBD_INTEN_USBEVENT_Msk )
+  {
+    uint32_t const evt_cause = NRF_USBD->EVENTCAUSE & (USBD_EVENTCAUSE_SUSPEND_Msk | USBD_EVENTCAUSE_RESUME_Msk);
+    NRF_USBD->EVENTCAUSE = evt_cause; // clear interrupt
+
+    if ( evt_cause & USBD_EVENTCAUSE_SUSPEND_Msk )
+    {
+      dcd_event_bus_signal(0, DCD_EVENT_SUSPEND, true);
+
+      // Put controller into low power mode
+      NRF_USBD->LOWPOWER = 1;
+
+      // Leave HFXO disable to application, since it may be used by other
+    }
+
+    if ( evt_cause & USBD_EVENTCAUSE_RESUME_Msk  )
+    {
+      dcd_event_bus_signal(0, DCD_EVENT_RESUME , true);
+    }
+  }
+
   if ( int_status & EDPT_END_ALL_MASK )
   {
     // DMA complete move data from SRAM -> Endpoint
     edpt_dma_end();
   }
-
+ 
   // Setup tokens are specific to the Control endpoint.
   if ( int_status & USBD_INTEN_EP0SETUP_Msk )
   {
@@ -502,12 +549,6 @@ void USBD_IRQHandler(void)
       }
     }
   }
-
-  // SOF interrupt
-  if ( int_status & USBD_INTEN_SOF_Msk )
-  {
-    dcd_event_bus_signal(0, DCD_EVENT_SOF, true);
-  }
 }
 
 #endif

+ 2 - 2
src/portable/nordic/nrf5x/hal_nrf5x.c

@@ -212,8 +212,8 @@ void tusb_hal_nrf_power_event (uint32_t event)
 
       nrf_usbd_isosplit_set(USBD_ISOSPLIT_SPLIT_HalfIN);
 
-      // Enable interrupt. SOF is used as CDC auto flush
-      NRF_USBD->INTENSET = USBD_INTEN_USBRESET_Msk | USBD_INTEN_USBEVENT_Msk | USBD_INTEN_EPDATA_Msk |
+      // Enable interrupt
+      NRF_USBD->INTENSET = USBD_INTEN_USBRESET_Msk | USBD_INTEN_EPDATA_Msk |
           USBD_INTEN_EP0SETUP_Msk | USBD_INTEN_EP0DATADONE_Msk | USBD_INTEN_ENDEPIN0_Msk | USBD_INTEN_ENDEPOUT0_Msk;
 
       // Enable interrupt, priorities should be set by application

+ 22 - 19
src/portable/nxp/lpc11_13_15/dcd_lpc11_13_15.c

@@ -126,22 +126,31 @@ static inline uint8_t ep_addr2id(uint8_t endpoint_addr)
 //--------------------------------------------------------------------+
 // CONTROLLER API
 //--------------------------------------------------------------------+
-void dcd_int_enable(uint8_t rhport)
+void dcd_init(uint8_t rhport)
 {
   (void) rhport;
-  NVIC_EnableIRQ(USB0_IRQn);
+
+  LPC_USB->EPLISTSTART  = (uint32_t) _dcd.ep;
+  LPC_USB->DATABUFSTART = SRAM_REGION;
+
+  LPC_USB->INTSTAT      = LPC_USB->INTSTAT; // clear all pending interrupt
+  LPC_USB->INTEN        = INT_DEVICE_STATUS_MASK;
+  LPC_USB->DEVCMDSTAT  |= CMDSTAT_DEVICE_ENABLE_MASK | CMDSTAT_DEVICE_CONNECT_MASK |
+                          CMDSTAT_RESET_CHANGE_MASK | CMDSTAT_CONNECT_CHANGE_MASK | CMDSTAT_SUSPEND_CHANGE_MASK;
+
+  NVIC_ClearPendingIRQ(USB0_IRQn);
 }
 
-void dcd_int_disable(uint8_t rhport)
+void dcd_int_enable(uint8_t rhport)
 {
   (void) rhport;
-  NVIC_DisableIRQ(USB0_IRQn);
+  NVIC_EnableIRQ(USB0_IRQn);
 }
 
-void dcd_set_config(uint8_t rhport, uint8_t config_num)
+void dcd_int_disable(uint8_t rhport)
 {
   (void) rhport;
-  (void) config_num;
+  NVIC_DisableIRQ(USB0_IRQn);
 }
 
 void dcd_set_address(uint8_t rhport, uint8_t dev_addr)
@@ -153,21 +162,15 @@ void dcd_set_address(uint8_t rhport, uint8_t dev_addr)
   LPC_USB->DEVCMDSTAT |= dev_addr;
 }
 
-bool dcd_init(uint8_t rhport)
+void dcd_set_config(uint8_t rhport, uint8_t config_num)
 {
   (void) rhport;
+  (void) config_num;
+}
 
-  LPC_USB->EPLISTSTART  = (uint32_t) _dcd.ep;
-  LPC_USB->DATABUFSTART = SRAM_REGION;
-
-  LPC_USB->INTSTAT      = LPC_USB->INTSTAT; // clear all pending interrupt
-  LPC_USB->INTEN        = INT_DEVICE_STATUS_MASK;
-  LPC_USB->DEVCMDSTAT  |= CMDSTAT_DEVICE_ENABLE_MASK | CMDSTAT_DEVICE_CONNECT_MASK |
-                          CMDSTAT_RESET_CHANGE_MASK | CMDSTAT_CONNECT_CHANGE_MASK | CMDSTAT_SUSPEND_CHANGE_MASK;
-
-  NVIC_EnableIRQ(USB0_IRQn);
-
-  return true;
+void dcd_remote_wakeup(uint8_t rhport)
+{
+  (void) rhport;
 }
 
 //--------------------------------------------------------------------+
@@ -339,7 +342,7 @@ void USB_IRQHandler(void)
         // Note: Host may delay more than 3 ms before and/or after bus reset before doing enumeration.
         if (dev_cmd_stat & CMDSTAT_DEVICE_ADDR_MASK)
         {
-          dcd_event_bus_signal(0, DCD_EVENT_SUSPENDED, true);
+          dcd_event_bus_signal(0, DCD_EVENT_SUSPEND, true);
         }
       }
     }

+ 7 - 5
src/portable/nxp/lpc17_40/dcd_lpc17_40.c

@@ -166,7 +166,7 @@ static void bus_reset(void)
   tu_memclr(&_dcd, sizeof(dcd_data_t));
 }
 
-bool dcd_init(uint8_t rhport)
+void dcd_init(uint8_t rhport)
 {
   (void) rhport;
 
@@ -185,9 +185,6 @@ bool dcd_init(uint8_t rhport)
 
   // USB IRQ priority should be set by application previously
   NVIC_ClearPendingIRQ(USB_IRQn);
-  NVIC_EnableIRQ(USB_IRQn);
-
-  return true;
 }
 
 void dcd_int_enable(uint8_t rhport)
@@ -217,6 +214,11 @@ void dcd_set_config(uint8_t rhport, uint8_t config_num)
   sie_write(SIE_CMDCODE_CONFIGURE_DEVICE, 1, 1);
 }
 
+void dcd_remote_wakeup(uint8_t rhport)
+{
+  (void) rhport;
+}
+
 //--------------------------------------------------------------------+
 // CONTROL HELPER
 //--------------------------------------------------------------------+
@@ -481,7 +483,7 @@ static void bus_event_isr(uint8_t rhport)
   {
     if (dev_status & SIE_DEV_STATUS_SUSPEND_MASK)
     {
-      dcd_event_bus_signal(rhport, DCD_EVENT_SUSPENDED, true);
+      dcd_event_bus_signal(rhport, DCD_EVENT_SUSPEND, true);
     }
     else
     {

+ 7 - 4
src/portable/nxp/lpc18_43/dcd_lpc18_43.c

@@ -121,7 +121,7 @@ static void bus_reset(uint8_t rhport)
 	p_dcd->qhd[0].int_on_setup = 1; // OUT only
 }
 
-bool dcd_init(uint8_t rhport)
+void dcd_init(uint8_t rhport)
 {
   LPC_USBHS_T* const lpc_usb = LPC_USB[rhport];
   dcd_data_t* p_dcd = dcd_data_ptr[rhport];
@@ -134,8 +134,6 @@ bool dcd_init(uint8_t rhport)
 
   lpc_usb->USBCMD_D &= ~0x00FF0000; // Interrupt Threshold Interval = 0
   lpc_usb->USBCMD_D |= TU_BIT(0); // connect
-
-  return true;
 }
 
 void dcd_int_enable(uint8_t rhport)
@@ -163,6 +161,11 @@ void dcd_set_config(uint8_t rhport, uint8_t config_num)
   // nothing to do
 }
 
+void dcd_remote_wakeup(uint8_t rhport)
+{
+  (void) rhport;
+}
+
 //--------------------------------------------------------------------+
 // HELPER
 //--------------------------------------------------------------------+
@@ -303,7 +306,7 @@ void hal_dcd_isr(uint8_t rhport)
       // Note: Host may delay more than 3 ms before and/or after bus reset before doing enumeration.
       if ((lpc_usb->DEVICEADDR >> 25) & 0x0f)
       {
-        dcd_event_bus_signal(rhport, DCD_EVENT_SUSPENDED, true);
+        dcd_event_bus_signal(rhport, DCD_EVENT_SUSPEND, true);
       }
     }
   }

+ 9 - 3
src/portable/st/stm32f3/dcd_stm32f3.c

@@ -35,10 +35,8 @@
 // MACRO TYPEDEF CONSTANT ENUM DECLARATION
 //--------------------------------------------------------------------+
 
-
-bool dcd_init (uint8_t rhport)
+void dcd_init (uint8_t rhport)
 {
-  return true;
 }
 
 // Enable device interrupt
@@ -57,6 +55,14 @@ void dcd_set_address(uint8_t rhport, uint8_t dev_addr)
 void dcd_set_config (uint8_t rhport, uint8_t config_num)
 {}
 
+void dcd_remote_wakeup(uint8_t rhport)
+{
+  (void) rhport;
+}
+
+//--------------------------------------------------------------------+
+// Endpoint API
+//--------------------------------------------------------------------+
 bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc)
 {
   return false;

+ 6 - 3
src/portable/st/stm32f4/dcd_stm32f4.c

@@ -128,7 +128,7 @@ static void end_of_reset(void) {
 /*------------------------------------------------------------------*/
 /* Controller API
  *------------------------------------------------------------------*/
-bool dcd_init (uint8_t rhport)
+void dcd_init (uint8_t rhport)
 {
   (void) rhport;
 
@@ -161,8 +161,6 @@ bool dcd_init (uint8_t rhport)
 
   // Enable pullup, enable peripheral.
   USB_OTG_FS->GCCFG |= USB_OTG_GCCFG_VBUSBSEN | USB_OTG_GCCFG_PWRDWN;
-
-  return true;
 }
 
 void dcd_int_enable (uint8_t rhport)
@@ -196,6 +194,11 @@ void dcd_set_config (uint8_t rhport, uint8_t config_num)
   // Nothing to do
 }
 
+void dcd_remote_wakeup(uint8_t rhport)
+{
+  (void) rhport;
+}
+
 /*------------------------------------------------------------------*/
 /* DCD Endpoint port
  *------------------------------------------------------------------*/