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

Merge pull request #61 from hathach/develop

enhance HID, add HID out endpoint support
hathach 6 лет назад
Родитель
Сommit
867e69ed9d
46 измененных файлов с 1329 добавлено и 726 удалено
  1. 2 1
      README.md
  2. 1 0
      docs/boards.md
  3. 8 178
      examples/device/cdc_msc_hid/Makefile
  4. 11 4
      examples/device/cdc_msc_hid/src/main.c
  5. 6 9
      examples/device/cdc_msc_hid/src/tusb_config.h
  6. 12 9
      examples/device/cdc_msc_hid/src/usb_descriptors.c
  7. 29 0
      examples/device/cdc_msc_hid_freertos/Makefile
  8. 0 1
      examples/device/cdc_msc_hid_freertos/ses/nrf5x/nrf5x.emProject
  9. 0 7
      examples/device/cdc_msc_hid_freertos/src/freertos_hook.c
  10. 11 4
      examples/device/cdc_msc_hid_freertos/src/main.c
  11. 6 9
      examples/device/cdc_msc_hid_freertos/src/tusb_config.h
  12. 7 4
      examples/device/cdc_msc_hid_freertos/src/usb_descriptors.c
  13. 15 0
      examples/device/hid_generic_inout/Makefile
  14. 15 0
      examples/device/hid_generic_inout/hid_test.py
  15. 154 0
      examples/device/hid_generic_inout/src/main.c
  16. 88 0
      examples/device/hid_generic_inout/src/tusb_config.h
  17. 117 0
      examples/device/hid_generic_inout/src/usb_descriptors.c
  18. 8 179
      examples/device/msc_dual_lun/Makefile
  19. 1 3
      examples/device/msc_dual_lun/src/tusb_config.h
  20. 3 5
      examples/device/msc_dual_lun/src/usb_descriptors.c
  21. 65 0
      examples/make.mk
  22. 111 0
      examples/rules.mk
  23. 54 0
      hw/bsp/feather_nrf52840_express/board.mk
  24. 197 0
      hw/bsp/feather_nrf52840_express/board_feather_nrf52840_express.c
  25. 15 9
      hw/bsp/metro_m0_express/board.mk
  26. 15 9
      hw/bsp/metro_m4_express/board.mk
  27. 17 12
      hw/bsp/pca10056/board.mk
  28. 10 4
      hw/bsp/stm32f303disc/board.mk
  29. 8 2
      hw/bsp/stm32f407g_disc1/board.mk
  30. 1 1
      hw/mcu/nordic/FreeRTOSConfig.h
  31. 1 1
      hw/mcu/nordic/nrfx
  32. 4 6
      src/class/cdc/cdc_device.c
  33. 8 0
      src/class/cdc/cdc_device.h
  34. 56 55
      src/class/hid/hid.h
  35. 45 61
      src/class/hid/hid_device.c
  36. 97 73
      src/class/hid/hid_device.h
  37. 11 13
      src/class/msc/msc_device.c
  38. 8 6
      src/class/msc/msc_device.h
  39. 2 2
      src/common/tusb_types.h
  40. 10 10
      src/device/usbd.c
  41. 18 2
      src/device/usbd.h
  42. 1 2
      src/device/usbd_pvt.h
  43. 30 37
      src/osal/osal.h
  44. 2 2
      src/portable/nordic/nrf5x/dcd_nrf5x.c
  45. 2 2
      tests/support/descriptor_test.c
  46. 47 4
      tools/build_all.py

+ 2 - 1
README.md

@@ -24,7 +24,7 @@ TinyUSB is an open-source cross-platform USB Host/Device stack for embedded syst
 Support multiple device configurations by dynamically changing usb descriptors. Low power functions such as suspend, resume and remote wakeup. Following device classes are supported:
 
 - Communication Class (CDC)
-- Human Interface Device (HID): Keyboard, Mouse, Gamepad etc ...
+- Human Interface Device (HID): Generic (In & Out), Keyboard, Mouse, Gamepad etc ...
 - Mass Storage Class (MSC): with multiple LUNs
 - Musical Instrument Digital Interface (MIDI)
 
@@ -78,5 +78,6 @@ TinyUSB is currently used by these other projects:
 * [Adafruit nRF52 Arduino](https://github.com/adafruit/Adafruit_nRF52_Arduino)
 * [Adafruit nRF52 Bootloader](https://github.com/adafruit/Adafruit_nRF52_Bootloader)
 * [CircuitPython](https://github.com/adafruit/circuitpython)
+* [TinyUSB Arduino Library](https://github.com/adafruit/Adafruit_TinyUSB_Arduino)
 
 If your project also uses TinyUSB and want to share, feel free to create a pull request.

+ 1 - 0
docs/boards.md

@@ -11,6 +11,7 @@ This code base already had supported for a handful of following boards
 
 ### Nordic nRF5x
 
+- [Adafruit Feather nRF52840 Express](https://www.adafruit.com/product/4062)
 - [nRF52840-DK (aka pca10056)](https://www.nordicsemi.com/Software-and-Tools/Development-Kits/nRF52840-DK)
 
 ### NXP LPC

+ 8 - 178
examples/device/cdc_msc_hid/Makefile

@@ -1,185 +1,15 @@
 include ../../../tools/top.mk
+include ../../make.mk
 
-# Select the board to build for.
-ifeq ($(BOARD),)
-  $(info You must provide a BOARD parameter with 'BOARD=')
-  $(info Possible values are:)
-  $(info $(sort $(subst /.,,$(subst $(TOP)/hw/bsp/,,$(wildcard $(TOP)/hw/bsp/*/.)))))
-  $(error BOARD not defined)
-else
-  ifeq ($(wildcard $(TOP)/hw/bsp/$(BOARD)/.),)
-    $(error Invalid BOARD specified)
-  endif
-endif
-
-# Verbose mode (V=). 0: default, 1: print out CFLAG, LDFLAG 2: print all compile command
-ifeq ("$(V)","2")
-  QUIET =
-else
-  QUIET = @
-endif
-
-# If the build directory is not given, make it reflect the board name.
-BUILD ?= build-$(BOARD)
-
-CROSS_COMPILE = arm-none-eabi-
-
-include $(TOP)/hw/bsp/$(BOARD)/board.mk
-
-CC = $(CROSS_COMPILE)gcc
-CXX = $(CROSS_COMPILE)g++
-OBJCOPY = $(CROSS_COMPILE)objcopy
-SIZE = $(CROSS_COMPILE)size
-MKDIR = mkdir
-SED = sed
-CP = cp
-RM = rm
-
-INC += -Isrc \
-	-I$(TOP)/hw \
-	-I$(TOP)/src
-
-CFLAGS += \
-	-fsingle-precision-constant \
-	-fno-strict-aliasing \
-	-Wdouble-promotion \
-	-Wno-endif-labels \
-	-Wstrict-prototypes \
-	-Werror-implicit-function-declaration \
-	-Wfloat-equal \
-	-Wundef \
-	-Wshadow \
-	-Wwrite-strings \
-	-Wsign-compare \
-	-Wmissing-format-attribute \
-	-Wno-deprecated-declarations \
-	-Wnested-externs \
-	-Wunreachable-code \
-	-Wno-error=lto-type-mismatch \
-	-ffunction-sections \
-	-fdata-sections
-
-# This causes lots of warning with nrf5x build due to nrfx code
-# CFLAGS += -Wcast-align
-
-#Debugging/Optimization
-ifeq ($(DEBUG), 1)
-  CFLAGS += -O0 -ggdb
-else
-  CFLAGS += -flto -Os
-endif
-
-CFLAGS += $(INC) -Wall -Werror -std=gnu11 -DBOARD_$(shell echo $(BOARD) | tr '[:lower:]' '[:upper:]')
-LDFLAGS += $(CFLAGS) -fshort-enums -Wl,-T,$(TOP)/$(LD_FILE) -Wl,-Map=$@.map -Wl,-cref -Wl,-gc-sections -specs=nosys.specs -specs=nano.specs
-
-ifeq ("$(V)","1")
-$(info CFLAGS  $(CFLAGS))
-$(info )
-$(info LDFLAGS $(LDFLAGS))
-$(info )
-$(info ASFLAGS $(ASFLAGS))
-$(info )
-endif
-
-LIBS = -lgcc -lc -lm -lnosys
+INC += \
+	src \
+	$(TOP)/hw \
 
+# Example source
 EXAMPLE_SOURCE += $(wildcard src/*.c)
 SRC_C += $(addprefix $(CURRENT_PATH)/, $(EXAMPLE_SOURCE))
 
-LIB_SOURCE += \
-	hw/bsp/$(BOARD)/board_$(BOARD).c \
-	src/common/tusb_fifo.c \
-	src/device/usbd.c \
-	src/device/usbd_control.c \
-	src/class/msc/msc_device.c \
-	src/class/cdc/cdc_device.c \
-	src/class/hid/hid_device.c \
-	src/tusb.c \
-	src/portable/$(VENDOR)/$(CHIP_FAMILY)/dcd_$(CHIP_FAMILY).c
-
-SRC_C += $(LIB_SOURCE)
-
-# Assembly files can be name with upper case .S, convert it to .s 
-SRC_S := $(SRC_S:.S=.s)
-
-# Due to GCC LTO bug https://bugs.launchpad.net/gcc-arm-embedded/+bug/1747966
-# assembly file should be placed first in linking order
-OBJ += $(addprefix $(BUILD)/obj/, $(SRC_S:.s=.o))
-OBJ += $(addprefix $(BUILD)/obj/, $(SRC_C:.c=.o))
-
-# Set all as default goal
-.DEFAULT_GOAL := all
-all: $(BUILD)/$(BOARD)-firmware.bin size
-
-OBJ_DIRS = $(sort $(dir $(OBJ)))
-$(OBJ): | $(OBJ_DIRS)
-$(OBJ_DIRS):
-	@$(MKDIR) -p $@
-
-$(BUILD)/$(BOARD)-firmware.elf: $(OBJ)
-	@echo LINK $@
-	$(QUIET)$(CC) -o $@ $(LDFLAGS) $^ -Wl,--start-group $(LIBS) -Wl,--end-group
-
-$(BUILD)/$(BOARD)-firmware.bin: $(BUILD)/$(BOARD)-firmware.elf
-	@echo CREATE $@
-	@$(OBJCOPY) -O binary -j .vectors -j .text -j .data $^ $@
-	
-$(BUILD)/$(BOARD)-firmware.hex: $(BUILD)/$(BOARD)-firmware.elf	
-	@echo CREATE $@
-	@$(OBJCOPY) -O ihex $^ $@
-
-# We set vpath to point to the top of the tree so that the source files
-# can be located. By following this scheme, it allows a single build rule
-# to be used to compile all .c files.
-vpath %.c . $(TOP)
-$(BUILD)/obj/%.o: %.c
-	@echo CC $(notdir $@)
-	$(QUIET)$(CC) $(CFLAGS) -c -MD -o $@ $<
-	@# The following fixes the dependency file.
-	@# See http://make.paulandlesley.org/autodep.html for details.
-	@# Regex adjusted from the above to play better with Windows paths, etc.
-	@$(CP) $(@:.o=.d) $(@:.o=.P); \
-	  $(SED) -e 's/#.*//' -e 's/^.*:  *//' -e 's/ *\\$$//' \
-	      -e '/^$$/ d' -e 's/$$/ :/' < $(@:.o=.d) >> $(@:.o=.P); \
-	  $(RM) $(@:.o=.d)
-
-# ASM sources lower case .s
-vpath %.s . $(TOP)
-$(BUILD)/obj/%.o: %.s
-	@echo AS $(notdir $@)
-	$(QUIET)$(CC) -x assembler-with-cpp $(ASFLAGS) -c -o $@ $<
-
-# ASM sources upper case .S
-vpath %.S . $(TOP)
-$(BUILD)/obj/%.o: %.S
-	@echo AS $(notdir $@)
-	$(QUIET)$(CC) -x assembler-with-cpp $(ASFLAGS) -c -o $@ $<
-
-# Flash binary using Jlink, should be added into system path 
-ifeq ($(OS),Windows_NT)
-  JLINKEXE = JLink.exe
-else 
-  JLINKEXE = JLinkExe
-endif
-
-# default jlink interface is swd
-ifeq ($(JLINK_IF),)
-  JLINK_IF = swd
-endif
-
-# Flash using jlink
-flash-jlink: $(BUILD)/$(BOARD)-firmware.hex
-	@echo halt > $(BUILD)/$(BOARD).jlink
-	@echo loadfile $^ >> $(BUILD)/$(BOARD).jlink
-	@echo r >> $(BUILD)/$(BOARD).jlink
-	@echo go >> $(BUILD)/$(BOARD).jlink
-	@echo exit >> $(BUILD)/$(BOARD).jlink
-	$(JLINKEXE) -device $(JLINK_DEVICE) -if $(JLINK_IF) -speed auto -CommandFile $(BUILD)/$(BOARD).jlink
-
-size: $(BUILD)/$(BOARD)-firmware.elf
-	-@echo ''
-	@$(SIZE) $<
-	-@echo ''
+# Board source
+SRC_C += hw/bsp/$(BOARD)/board_$(BOARD).c
 
-clean:
-	rm -rf build-$(BOARD)
+include ../../rules.mk

+ 11 - 4
examples/device/cdc_msc_hid/src/main.c

@@ -49,8 +49,8 @@ static uint32_t blink_interval_ms = BLINK_NOT_MOUNTED;
 
 void led_blinking_task(void);
 
-extern void cdc_task(void);
-extern void hid_task(void);
+void cdc_task(void);
+void hid_task(void);
 
 /*------------- MAIN -------------*/
 int main(void)
@@ -196,7 +196,9 @@ void hid_task(void)
     if ( btn )
     {
       int8_t const delta = 5;
-      tud_hid_mouse_move(REPORT_ID_MOUSE, delta, delta); // right + down
+
+      // no button, right + down, no scroll pan
+      tud_hid_mouse_report(REPORT_ID_MOUSE, 0x00, delta, delta, 0, 0);
 
       // delay a bit before attempt to send keyboard report
       board_delay(2);
@@ -220,12 +222,15 @@ void hid_task(void)
     }else
     {
       // send empty key report if previously has key pressed
-      if (has_key) tud_hid_keyboard_key_release(REPORT_ID_KEYBOARD);
+      if (has_key) tud_hid_keyboard_report(REPORT_ID_KEYBOARD, 0, NULL);
       has_key = false;
     }
   }
 }
 
+// Invoked when received GET_REPORT control request
+// Application must fill buffer report's content and return its length.
+// Return zero will cause the stack to STALL request
 uint16_t tud_hid_get_report_cb(uint8_t report_id, hid_report_type_t report_type, uint8_t* buffer, uint16_t reqlen)
 {
   // TODO not Implemented
@@ -237,6 +242,8 @@ uint16_t tud_hid_get_report_cb(uint8_t report_id, hid_report_type_t report_type,
   return 0;
 }
 
+// Invoked when received SET_REPORT control request or
+// received data on OUT endpoint ( Report ID = 0, Type = 0 )
 void tud_hid_set_report_cb(uint8_t report_id, hid_report_type_t report_type, uint8_t const* buffer, uint16_t bufsize)
 {
   // TODO not Implemented

+ 6 - 9
examples/device/cdc_msc_hid/src/tusb_config.h

@@ -77,17 +77,13 @@
 #define CFG_TUD_MIDI                0
 #define CFG_TUD_CUSTOM_CLASS        0
 
-//--------------------------------------------------------------------
-// CDC
-//--------------------------------------------------------------------
+//------------- CDC -------------//
 
 // FIFO size of CDC TX and RX
 #define CFG_TUD_CDC_RX_BUFSIZE      64
 #define CFG_TUD_CDC_TX_BUFSIZE      64
 
-//--------------------------------------------------------------------
-// MSC
-//--------------------------------------------------------------------
+//------------- MSC -------------//
 
 // Buffer size of Device Mass storage
 #define CFG_TUD_MSC_BUFSIZE         512
@@ -101,9 +97,10 @@
 // Product revision string included in Inquiry response, max 4 bytes
 #define CFG_TUD_MSC_PRODUCT_REV     "1.0"
 
-//--------------------------------------------------------------------
-// HID
-//--------------------------------------------------------------------
+//------------- HID -------------//
+
+// Should be sufficient to hold ID (if any) + Data
+#define CFG_TUD_HID_BUFSIZE         16
 
 #ifdef __cplusplus
  }

+ 12 - 9
examples/device/cdc_msc_hid/src/usb_descriptors.c

@@ -76,8 +76,8 @@ enum
 
 uint8_t const desc_hid_report[] =
 {
-  HID_REPORT_DESC_KEYBOARD( HID_REPORT_ID(REPORT_ID_KEYBOARD), ),
-  HID_REPORT_DESC_MOUSE   ( HID_REPORT_ID(REPORT_ID_MOUSE), )
+  TUD_HID_REPORT_DESC_KEYBOARD( HID_REPORT_ID(REPORT_ID_KEYBOARD), ),
+  TUD_HID_REPORT_DESC_MOUSE   ( HID_REPORT_ID(REPORT_ID_MOUSE), )
 };
 #endif
 
@@ -117,19 +117,22 @@ enum
 
 uint8_t const desc_configuration[] =
 {
-  // Config: self-powered with remote wakeup support, max power up to 100 mA
+  // Inteface count, string index, total length, attribute, power in mA
   TUD_CONFIG_DESCRIPTOR(ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100),
 
 #if CFG_TUD_CDC
+  // 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, 0x02, 0x82, 64),
 #endif
 
 #if CFG_TUD_MSC
+  // Interface number, string index, EP Out & EP In address, EP size
   TUD_MSC_DESCRIPTOR(ITF_NUM_MSC, 5, EPNUM_MSC, 0x80 | EPNUM_MSC, 64), // highspeed 512
 #endif
 
 #if CFG_TUD_HID
-  TUD_HID_DESCRIPTOR(ITF_NUM_HID, 6, HID_PROTOCOL_KEYBOARD, sizeof(desc_hid_report), 0x84, 16, 10)
+  // Interface number, string index, protocol, report descriptor len, EP In address, size & polling interval
+  TUD_HID_DESCRIPTOR(ITF_NUM_HID, 6, HID_PROTOCOL_NONE, sizeof(desc_hid_report), 0x84, 16, 10)
 #endif
 };
 
@@ -162,13 +165,13 @@ uint16_t const * const string_desc_arr [] =
 // tud_desc_set is required by tinyusb stack
 tud_desc_set_t tud_desc_set =
 {
-    .device     = &desc_device,
-    .config     = desc_configuration,
+  .device     = &desc_device,
+  .config     = desc_configuration,
 
-    .string_arr   = (uint8_t const **) string_desc_arr,
-    .string_count = sizeof(string_desc_arr)/sizeof(string_desc_arr[0]),
+  .string_arr   = (uint8_t const **) string_desc_arr,
+  .string_count = sizeof(string_desc_arr)/sizeof(string_desc_arr[0]),
 
 #if CFG_TUD_HID
-    .hid_report = desc_hid_report,
+  .hid_report = desc_hid_report,
 #endif
 };

+ 29 - 0
examples/device/cdc_msc_hid_freertos/Makefile

@@ -0,0 +1,29 @@
+include ../../../tools/top.mk
+include ../../make.mk
+
+INC += \
+	src \
+	$(TOP)/hw \
+	$(TOP)/lib/FreeRTOS/Source/include \
+	$(TOP)/lib/FreeRTOS/Source/portable/GCC/$(FREERTOS_PORT)
+	
+# Example source
+EXAMPLE_SOURCE += $(wildcard src/*.c)
+SRC_C += $(addprefix $(CURRENT_PATH)/, $(EXAMPLE_SOURCE))
+
+# Board source
+SRC_C += hw/bsp/$(BOARD)/board_$(BOARD).c
+
+# FreeRTOS source
+SRC_C += \
+	lib/FreeRTOS/Source/list.c \
+	lib/FreeRTOS/Source/queue.c \
+	lib/FreeRTOS/Source/tasks.c \
+	lib/FreeRTOS/Source/timers.c \
+	lib/FreeRTOS/Source/portable/MemMang/heap_4.c \
+	lib/FreeRTOS/Source/portable/GCC/$(FREERTOS_PORT)/port.c \
+
+# FreeRTOS (lto + Os) linker issue
+LDFLAGS += -Wl,--undefined=vTaskSwitchContext
+
+include ../../rules.mk

+ 0 - 1
examples/device/cdc_msc_hid_freertos/ses/nrf5x/nrf5x.emProject

@@ -141,7 +141,6 @@
           <file file_name="../../../../../lib/FreeRTOS/Source/tasks.c" />
           <file file_name="../../../../../lib/FreeRTOS/Source/timers.c" />
         </folder>
-        <file file_name="../../../../../lib/FreeRTOS/freertos_hook.c" />
       </folder>
     </folder>
     <configuration

+ 0 - 7
lib/FreeRTOS/freertos_hook.c → examples/device/cdc_msc_hid_freertos/src/freertos_hook.c

@@ -94,10 +94,3 @@ void vApplicationGetTimerTaskMemory( StaticTask_t **ppxTimerTaskTCBBuffer, Stack
     configTIMER_TASK_STACK_DEPTH is specified in words, not bytes. */
   *pulTimerTaskStackSize = configTIMER_TASK_STACK_DEPTH;
 }
-
-
-// executes from within an ISR
-void vApplicationTickHook(void)
-{
-
-}

+ 11 - 4
examples/device/cdc_msc_hid_freertos/src/main.c

@@ -55,6 +55,8 @@ TimerHandle_t blink_tm;
 
 void led_blinky_cb(TimerHandle_t xTimer);
 void usb_device_task(void* param);
+void cdc_task(void* params);
+void hid_task(void* params);
 
 /*------------- MAIN -------------*/
 int main(void)
@@ -72,12 +74,10 @@ int main(void)
 
   // Create task
 #if CFG_TUD_CDC
-  extern void cdc_task(void* params);
   xTaskCreate( cdc_task, "cdc", 128, NULL, configMAX_PRIORITIES-2, NULL);
 #endif
 
 #if CFG_TUD_HID
-  extern void hid_task(void* params);
   xTaskCreate( hid_task, "hid", 128, NULL, configMAX_PRIORITIES-2, NULL);
 #endif
 
@@ -226,7 +226,9 @@ void hid_task(void* params)
       if ( btn )
       {
         int8_t const delta = 5;
-        tud_hid_mouse_move(REPORT_ID_MOUSE, delta, delta); // right + down
+
+        // no button, right + down, no scroll pan
+        tud_hid_mouse_report(REPORT_ID_MOUSE, 0x00, delta, delta, 0, 0);
 
         // delay a bit before attempt to send keyboard report
         vTaskDelay(pdMS_TO_TICKS(2));
@@ -250,13 +252,16 @@ void hid_task(void* params)
       }else
       {
         // send empty key report if previously has key pressed
-        if (has_key) tud_hid_keyboard_key_release(REPORT_ID_KEYBOARD);
+        if (has_key) tud_hid_keyboard_report(REPORT_ID_KEYBOARD, 0, NULL);
         has_key = false;
       }
     }
   }
 }
 
+// Invoked when received GET_REPORT control request
+// Application must fill buffer report's content and return its length.
+// Return zero will cause the stack to STALL request
 uint16_t tud_hid_get_report_cb(uint8_t report_id, hid_report_type_t report_type, uint8_t* buffer, uint16_t reqlen)
 {
   // TODO not Implemented
@@ -268,6 +273,8 @@ uint16_t tud_hid_get_report_cb(uint8_t report_id, hid_report_type_t report_type,
   return 0;
 }
 
+// Invoked when received SET_REPORT control request or
+// received data on OUT endpoint ( Report ID = 0, Type = 0 )
 void tud_hid_set_report_cb(uint8_t report_id, hid_report_type_t report_type, uint8_t const* buffer, uint16_t bufsize)
 {
   // TODO not Implemented

+ 6 - 9
examples/device/cdc_msc_hid_freertos/src/tusb_config.h

@@ -77,17 +77,13 @@
 #define CFG_TUD_MIDI                0
 #define CFG_TUD_CUSTOM_CLASS        0
 
-//--------------------------------------------------------------------
-// CDC
-//--------------------------------------------------------------------
+//------------- CDC -------------//
 
 // FIFO size of CDC TX and RX
 #define CFG_TUD_CDC_RX_BUFSIZE      64
 #define CFG_TUD_CDC_TX_BUFSIZE      64
 
-//--------------------------------------------------------------------
-// MSC
-//--------------------------------------------------------------------
+//------------- MSC -------------//
 
 // Buffer size of Device Mass storage
 #define CFG_TUD_MSC_BUFSIZE         512
@@ -101,9 +97,10 @@
 // Product revision string included in Inquiry response, max 4 bytes
 #define CFG_TUD_MSC_PRODUCT_REV     "1.0"
 
-//--------------------------------------------------------------------
-// HID
-//--------------------------------------------------------------------
+//------------- HID -------------//
+
+// Should be sufficient to hold ID (if any) + Data
+#define CFG_TUD_HID_BUFSIZE         16
 
 #ifdef __cplusplus
  }

+ 7 - 4
examples/device/cdc_msc_hid_freertos/src/usb_descriptors.c

@@ -76,8 +76,8 @@ enum
 
 uint8_t const desc_hid_report[] =
 {
-  HID_REPORT_DESC_KEYBOARD( HID_REPORT_ID(REPORT_ID_KEYBOARD), ),
-  HID_REPORT_DESC_MOUSE   ( HID_REPORT_ID(REPORT_ID_MOUSE), )
+  TUD_HID_REPORT_DESC_KEYBOARD( HID_REPORT_ID(REPORT_ID_KEYBOARD), ),
+  TUD_HID_REPORT_DESC_MOUSE   ( HID_REPORT_ID(REPORT_ID_MOUSE), )
 };
 #endif
 
@@ -117,19 +117,22 @@ enum
 
 uint8_t const desc_configuration[] =
 {
-  // Config: self-powered with remote wakeup support, max power up to 100 mA
+  // Inteface count, string index, total length, attribute, power in mA
   TUD_CONFIG_DESCRIPTOR(ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100),
 
 #if CFG_TUD_CDC
+  // 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, 0x02, 0x82, 64),
 #endif
 
 #if CFG_TUD_MSC
+  // Interface number, string index, EP Out & EP In address, EP size
   TUD_MSC_DESCRIPTOR(ITF_NUM_MSC, 5, EPNUM_MSC, 0x80 | EPNUM_MSC, 64), // highspeed 512
 #endif
 
 #if CFG_TUD_HID
-  TUD_HID_DESCRIPTOR(ITF_NUM_HID, 6, HID_PROTOCOL_KEYBOARD, sizeof(desc_hid_report), 0x84, 16, 10)
+  // Interface number, string index, protocol, report descriptor len, EP In address, size & polling interval
+  TUD_HID_DESCRIPTOR(ITF_NUM_HID, 6, HID_PROTOCOL_NONE, sizeof(desc_hid_report), 0x84, 16, 10)
 #endif
 };
 

+ 15 - 0
examples/device/hid_generic_inout/Makefile

@@ -0,0 +1,15 @@
+include ../../../tools/top.mk
+include ../../make.mk
+
+INC += \
+	src \
+	$(TOP)/hw \
+
+# Example source
+EXAMPLE_SOURCE += $(wildcard src/*.c)
+SRC_C += $(addprefix $(CURRENT_PATH)/, $(EXAMPLE_SOURCE))
+
+# Board source
+SRC_C += hw/bsp/$(BOARD)/board_$(BOARD).c
+
+include ../../rules.mk

+ 15 - 0
examples/device/hid_generic_inout/hid_test.py

@@ -0,0 +1,15 @@
+# Install python3 HID package https://pypi.org/project/hid/
+import hid
+
+USB_VID = 0xcafe
+
+for dict in hid.enumerate(0xcafe):
+    print(dict)
+    dev = hid.Device(dict['vendor_id'], dict['product_id'])
+    if dev:
+        while True:
+            # Get input from console and encode to UTF8 for array of chars.
+            str_out = input("Send text to HID Device : ").encode('utf-8')
+            dev.write(str_out)
+            str_in = dev.read(64)
+            print("Received from HID Device:", str_in, '\n')

+ 154 - 0
examples/device/hid_generic_inout/src/main.c

@@ -0,0 +1,154 @@
+/* 
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2018, hathach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "bsp/board.h"
+#include "tusb.h"
+
+/* This example demonstrate HID Generic raw Input & Output.
+ * It will receive data from Host (In endpoint) and echo back (Out endpoint).
+ * HID Report descriptor use vendor for usage page (using template TUD_HID_REPORT_DESC_GENERIC_INOUT)
+ *
+ * Run 'python3 hid_test.py' on your PC to send and receive data to this device.
+ * Python and `hid` package is required, for installation please follow
+ * https://pypi.org/project/hid/
+ */
+
+//--------------------------------------------------------------------+
+// MACRO CONSTANT TYPEDEF PROTYPES
+//--------------------------------------------------------------------+
+
+/* Blink pattern
+ * - 250 ms  : device not mounted
+ * - 1000 ms : device mounted
+ * - 2500 ms : device is suspended
+ */
+enum  {
+  BLINK_NOT_MOUNTED = 250,
+  BLINK_MOUNTED = 1000,
+  BLINK_SUSPENDED = 2500,
+};
+
+static uint32_t blink_interval_ms = BLINK_NOT_MOUNTED;
+
+void led_blinking_task(void);
+
+/*------------- MAIN -------------*/
+int main(void)
+{
+  board_init();
+
+  tusb_init();
+
+  while (1)
+  {
+    // tinyusb device task
+    tud_task();
+
+    led_blinking_task();
+  }
+
+  return 0;
+}
+
+//--------------------------------------------------------------------+
+// Device callbacks
+//--------------------------------------------------------------------+
+
+// Invoked when device is mounted
+void tud_mount_cb(void)
+{
+  blink_interval_ms = BLINK_MOUNTED;
+}
+
+// Invoked when device is unmounted
+void tud_umount_cb(void)
+{
+  blink_interval_ms = BLINK_NOT_MOUNTED;
+}
+
+// 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 = BLINK_SUSPENDED;
+}
+
+// Invoked when usb bus is resumed
+void tud_resume_cb(void)
+{
+  blink_interval_ms = BLINK_MOUNTED;
+}
+
+//--------------------------------------------------------------------+
+// USB HID
+//--------------------------------------------------------------------+
+
+// Invoked when received GET_REPORT control request
+// Application must fill buffer report's content and return its length.
+// Return zero will cause the stack to STALL request
+uint16_t tud_hid_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;
+}
+
+// Invoked when received SET_REPORT control request or
+// received data on OUT endpoint ( Report ID = 0, Type = 0 )
+void tud_hid_set_report_cb(uint8_t report_id, hid_report_type_t report_type, uint8_t const* buffer, uint16_t bufsize)
+{
+  // This example doesn't use multiple report and report ID
+  (void) report_id;
+  (void) report_type;
+
+  // echo back anything we received from host
+  tud_hid_report(0, buffer, bufsize);
+}
+
+//--------------------------------------------------------------------+
+// BLINKING TASK
+//--------------------------------------------------------------------+
+void led_blinking_task(void)
+{
+  static uint32_t start_ms = 0;
+  static bool led_state = false;
+
+  // Blink every 1000 ms
+  if ( board_millis() < start_ms + blink_interval_ms) return; // not enough time
+  start_ms += blink_interval_ms;
+
+  board_led_write(led_state);
+  led_state = 1 - led_state; // toggle
+}

+ 88 - 0
examples/device/hid_generic_inout/src/tusb_config.h

@@ -0,0 +1,88 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2018, hathach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#ifndef _TUSB_CONFIG_H_
+#define _TUSB_CONFIG_H_
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+//--------------------------------------------------------------------
+// COMMON CONFIGURATION
+//--------------------------------------------------------------------
+
+// defined by compiler flags for flexibility
+#ifndef CFG_TUSB_MCU
+  #error CFG_TUSB_MCU must be defined
+#endif
+
+#if CFG_TUSB_MCU == OPT_MCU_LPC43XX || CFG_TUSB_MCU == OPT_MCU_LPC18XX
+#define CFG_TUSB_RHPORT0_MODE       (OPT_MODE_DEVICE | OPT_MODE_HIGH_SPEED)
+#else
+#define CFG_TUSB_RHPORT0_MODE       OPT_MODE_DEVICE
+#endif
+
+#define CFG_TUSB_OS                 OPT_OS_NONE
+#define CFG_TUSB_DEBUG              2
+
+/* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment.
+ * Tinyusb use follows macros to declare transferring memory so that they can be put
+ * into those specific section.
+ * e.g
+ * - CFG_TUSB_MEM SECTION : __attribute__ (( section(".usb_ram") ))
+ * - CFG_TUSB_MEM_ALIGN   : __attribute__ ((aligned(4)))
+ */
+#ifndef CFG_TUSB_MEM_SECTION
+#define CFG_TUSB_MEM_SECTION
+#endif
+
+#ifndef CFG_TUSB_MEM_ALIGN
+#define CFG_TUSB_MEM_ALIGN          ATTR_ALIGNED(4)
+#endif
+
+//--------------------------------------------------------------------
+// DEVICE CONFIGURATION
+//--------------------------------------------------------------------
+
+#define CFG_TUD_ENDOINT0_SIZE       64
+
+//------------- CLASS -------------//
+#define CFG_TUD_CDC                 0
+#define CFG_TUD_MSC                 0
+#define CFG_TUD_HID                 1
+#define CFG_TUD_MIDI                0
+#define CFG_TUD_CUSTOM_CLASS        0
+
+//------------- HID -------------//
+
+// Should be sufficient to hold ID (if any) + Data
+#define CFG_TUD_HID_BUFSIZE         64
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif /* _TUSB_CONFIG_H_ */

+ 117 - 0
examples/device/hid_generic_inout/src/usb_descriptors.c

@@ -0,0 +1,117 @@
+/* 
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2018, hathach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#include "tusb.h"
+
+/* A combination of interfaces must have a unique product id, since PC will save device driver after the first plug.
+ * Same VID/PID with different interface e.g MSC (first), then CDC (later) will possibly cause system error on PC.
+ *
+ * Auto ProductID layout's Bitmap:
+ *   [MSB]         HID | MSC | CDC          [LSB]
+ */
+#define _PID_MAP(itf, n)  ( (CFG_TUD_##itf) << (n) )
+#define USB_PID           (0x4000 | _PID_MAP(CDC, 0) | _PID_MAP(MSC, 1) | _PID_MAP(HID, 2) )
+
+//------------- Device Descriptors -------------//
+tusb_desc_device_t const desc_device =
+{
+    .bLength            = sizeof(tusb_desc_device_t),
+    .bDescriptorType    = TUSB_DESC_DEVICE,
+    .bcdUSB             = 0x0200,
+    .bDeviceClass       = 0x00,
+    .bDeviceSubClass    = 0x00,
+    .bDeviceProtocol    = 0x00,
+    .bMaxPacketSize0    = CFG_TUD_ENDOINT0_SIZE,
+
+    .idVendor           = 0xCafe,
+    .idProduct          = USB_PID,
+    .bcdDevice          = 0x0100,
+
+    .iManufacturer      = 0x01,
+    .iProduct           = 0x02,
+    .iSerialNumber      = 0x03,
+
+    .bNumConfigurations = 0x01
+};
+
+//------------- HID Report Descriptor -------------//
+uint8_t const desc_hid_report[] =
+{
+  TUD_HID_REPORT_DESC_GENERIC_INOUT(CFG_TUD_HID_BUFSIZE)
+};
+
+//------------- Configuration Descriptor -------------//
+enum
+{
+  ITF_NUM_HID,
+  ITF_NUM_TOTAL
+};
+
+enum
+{
+  CONFIG_TOTAL_LEN = TUD_CONFIG_DESC_LEN + TUD_HID_INOUT_DESC_LEN
+};
+
+// Use Endpoint 2 instead of 1 due to NXP MCU
+// LPC 17xx and 40xx endpoint type (bulk/interrupt/iso) are fixed by its number
+// 0 control, 1 In, 2 Bulk, 3 Iso, 4 In etc ...
+#define EPNUM_HID   0x01
+
+uint8_t const desc_configuration[] =
+{
+  // Inteface count, string index, total length, attribute, power in mA
+  TUD_CONFIG_DESCRIPTOR(ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 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_PROTOCOL_NONE, sizeof(desc_hid_report), 0x80 | EPNUM_HID, EPNUM_HID, 16, 10)
+};
+
+//------------- String Descriptors -------------//
+// array of pointer to string descriptors
+uint16_t const * const string_desc_arr [] =
+{
+  // 0: is supported language = English
+  TUD_DESC_STRCONV(0x0409),
+
+  // 1: Manufacturer
+  TUD_DESC_STRCONV('t', 'i', 'n', 'y', 'u', 's', 'b', '.', 'o', 'r', 'g'),
+
+  // 2: Product
+  TUD_DESC_STRCONV('t', 'i', 'n', 'y', 'u', 's', 'b', ' ', 'd', 'e', 'v', 'i', 'c', 'e'),
+
+  // 3: Serials, should use chip ID
+  TUD_DESC_STRCONV('1', '2', '3', '4', '5', '6'),
+};
+
+// tud_desc_set is required by tinyusb stack
+tud_desc_set_t tud_desc_set =
+{
+  .device       = &desc_device,
+  .config       = desc_configuration,
+
+  .string_arr   = (uint8_t const **) string_desc_arr,
+  .string_count = sizeof(string_desc_arr)/sizeof(string_desc_arr[0]),
+  .hid_report   = desc_hid_report,
+};

+ 8 - 179
examples/device/msc_dual_lun/Makefile

@@ -1,186 +1,15 @@
 include ../../../tools/top.mk
+include ../../make.mk
 
-# Select the board to build for.
-ifeq ($(BOARD),)
-  $(info You must provide a BOARD parameter with 'BOARD=')
-  $(info Possible values are:)
-  $(info $(sort $(subst /.,,$(subst $(TOP)/hw/bsp/,,$(wildcard $(TOP)/hw/bsp/*/.)))))
-  $(error BOARD not defined)
-else
-  ifeq ($(wildcard $(TOP)/hw/bsp/$(BOARD)/.),)
-    $(error Invalid BOARD specified)
-  endif
-endif
-
-# Verbose mode (V=). 0: default, 1: print out CFLAG, LDFLAG 2: print all compile command
-ifeq ("$(V)","2")
-  QUIET =
-else
-  QUIET = @
-endif
-
-# If the build directory is not given, make it reflect the board name.
-BUILD ?= build-$(BOARD)
-
-CROSS_COMPILE = arm-none-eabi-
-
-include $(TOP)/hw/bsp/$(BOARD)/board.mk
-
-CC = $(CROSS_COMPILE)gcc
-CXX = $(CROSS_COMPILE)g++
-OBJCOPY = $(CROSS_COMPILE)objcopy
-SIZE = $(CROSS_COMPILE)size
-MKDIR = mkdir
-SED = sed
-CP = cp
-RM = rm
-
-INC += -Isrc \
-	-I$(TOP)/hw \
-	-I$(TOP)/src
-
-CFLAGS += \
-	-fsingle-precision-constant \
-	-fno-strict-aliasing \
-	-Wdouble-promotion \
-	-Wno-endif-labels \
-	-Wstrict-prototypes \
-	-Werror-implicit-function-declaration \
-	-Wfloat-equal \
-	-Wundef \
-	-Wshadow \
-	-Wwrite-strings \
-	-Wsign-compare \
-	-Wmissing-format-attribute \
-	-Wno-deprecated-declarations \
-	-Wnested-externs \
-	-Wunreachable-code \
-	-Wno-error=lto-type-mismatch \
-	-ffunction-sections \
-	-fdata-sections
-
-# This causes lots of warning with nrf5x build due to nrfx code
-# CFLAGS += -Wcast-align
-
-#Debugging/Optimization
-ifeq ($(DEBUG), 1)
-  CFLAGS += -O0 -ggdb
-else
-  CFLAGS += -flto -Os
-endif
-
-CFLAGS += $(INC) -Wall -Werror -std=gnu11 -DBOARD_$(shell echo $(BOARD) | tr '[:lower:]' '[:upper:]')
-LDFLAGS += $(CFLAGS) -fshort-enums -Wl,-T,$(TOP)/$(LD_FILE) -Wl,-Map=$@.map -Wl,-cref -Wl,-gc-sections -specs=nosys.specs -specs=nano.specs
-
-ifeq ("$(V)","1")
-$(info CFLAGS  $(CFLAGS))
-$(info )
-$(info LDFLAGS $(LDFLAGS))
-$(info )
-$(info ASFLAGS $(ASFLAGS))
-$(info )
-endif
-
-LIBS = -lgcc -lc -lm -lnosys
+INC += \
+  src \
+  $(TOP)/hw \
 
+# Example source
 EXAMPLE_SOURCE += $(wildcard src/*.c)
-
 SRC_C += $(addprefix $(CURRENT_PATH)/, $(EXAMPLE_SOURCE))
 
-LIB_SOURCE += \
-	hw/bsp/$(BOARD)/board_$(BOARD).c \
-	src/common/tusb_fifo.c \
-	src/device/usbd.c \
-	src/device/usbd_control.c \
-	src/class/msc/msc_device.c \
-	src/class/cdc/cdc_device.c \
-	src/class/hid/hid_device.c \
-	src/tusb.c \
-	src/portable/$(VENDOR)/$(CHIP_FAMILY)/dcd_$(CHIP_FAMILY).c
-
-SRC_C += $(LIB_SOURCE)
-
-# Assembly files can be name with upper case .S, convert it to .s 
-SRC_S := $(SRC_S:.S=.s)
-
-# Due to GCC LTO bug https://bugs.launchpad.net/gcc-arm-embedded/+bug/1747966
-# assembly file should be placed first in linking order
-OBJ += $(addprefix $(BUILD)/obj/, $(SRC_S:.s=.o))
-OBJ += $(addprefix $(BUILD)/obj/, $(SRC_C:.c=.o))
-
-# Set all as default goal
-.DEFAULT_GOAL := all
-all: $(BUILD)/$(BOARD)-firmware.bin size
-
-OBJ_DIRS = $(sort $(dir $(OBJ)))
-$(OBJ): | $(OBJ_DIRS)
-$(OBJ_DIRS):
-	@$(MKDIR) -p $@
-
-$(BUILD)/$(BOARD)-firmware.elf: $(OBJ)
-	@echo LINK $@
-	$(QUIET)$(CC) -o $@ $(LDFLAGS) $^ -Wl,--start-group $(LIBS) -Wl,--end-group
-
-$(BUILD)/$(BOARD)-firmware.bin: $(BUILD)/$(BOARD)-firmware.elf
-	@echo CREATE $@
-	@$(OBJCOPY) -O binary -j .vectors -j .text -j .data $^ $@
-	
-$(BUILD)/$(BOARD)-firmware.hex: $(BUILD)/$(BOARD)-firmware.elf	
-	@echo CREATE $@
-	@$(OBJCOPY) -O ihex $^ $@
-
-# We set vpath to point to the top of the tree so that the source files
-# can be located. By following this scheme, it allows a single build rule
-# to be used to compile all .c files.
-vpath %.c . $(TOP)
-$(BUILD)/obj/%.o: %.c
-	@echo CC $(notdir $@)
-	$(QUIET)$(CC) $(CFLAGS) -c -MD -o $@ $<
-	@# The following fixes the dependency file.
-	@# See http://make.paulandlesley.org/autodep.html for details.
-	@# Regex adjusted from the above to play better with Windows paths, etc.
-	@$(CP) $(@:.o=.d) $(@:.o=.P); \
-	  $(SED) -e 's/#.*//' -e 's/^.*:  *//' -e 's/ *\\$$//' \
-	      -e '/^$$/ d' -e 's/$$/ :/' < $(@:.o=.d) >> $(@:.o=.P); \
-	  $(RM) $(@:.o=.d)
-
-# ASM sources lower case .s
-vpath %.s . $(TOP)
-$(BUILD)/obj/%.o: %.s
-	@echo AS $(notdir $@)
-	$(QUIET)$(CC) -x assembler-with-cpp $(ASFLAGS) -c -o $@ $<
-
-# ASM sources upper case .S
-vpath %.S . $(TOP)
-$(BUILD)/obj/%.o: %.S
-	@echo AS $(notdir $@)
-	$(QUIET)$(CC) -x assembler-with-cpp $(ASFLAGS) -c -o $@ $<
-
-# Flash binary using Jlink, should be added into system path 
-ifeq ($(OS),Windows_NT)
-  JLINKEXE = JLink.exe
-else 
-  JLINKEXE = JLinkExe
-endif
-
-# default jlink interface is swd
-ifeq ($(JLINK_IF),)
-  JLINK_IF = swd
-endif
-
-# Flash using jlink
-flash-jlink: $(BUILD)/$(BOARD)-firmware.hex
-	@echo halt > $(BUILD)/$(BOARD).jlink
-	@echo loadfile $^ >> $(BUILD)/$(BOARD).jlink
-	@echo r >> $(BUILD)/$(BOARD).jlink
-	@echo go >> $(BUILD)/$(BOARD).jlink
-	@echo exit >> $(BUILD)/$(BOARD).jlink
-	$(JLINKEXE) -device $(JLINK_DEVICE) -if $(JLINK_IF) -speed auto -CommandFile $(BUILD)/$(BOARD).jlink
-
-size: $(BUILD)/$(BOARD)-firmware.elf
-	-@echo ''
-	@$(SIZE) $<
-	-@echo ''
+# Board source
+SRC_C += hw/bsp/$(BOARD)/board_$(BOARD).c
 
-clean:
-	rm -rf build-$(BOARD)
+include ../../rules.mk

+ 1 - 3
examples/device/msc_dual_lun/src/tusb_config.h

@@ -76,9 +76,7 @@
 #define CFG_TUD_MIDI                0
 #define CFG_TUD_CUSTOM_CLASS        0
 
-//--------------------------------------------------------------------
-// MSC
-//--------------------------------------------------------------------
+//------------- MSC -------------//
 
 // Buffer size of Device Mass storage
 #define CFG_TUD_MSC_BUFSIZE         512

+ 3 - 5
examples/device/msc_dual_lun/src/usb_descriptors.c

@@ -43,7 +43,6 @@ tusb_desc_device_t const desc_device =
     .bDeviceClass       = 0x00,
     .bDeviceSubClass    = 0x00,
     .bDeviceProtocol    = 0x00,
-
     .bMaxPacketSize0    = CFG_TUD_ENDOINT0_SIZE,
 
     .idVendor           = 0xCafe,
@@ -76,12 +75,11 @@ enum
 
 uint8_t const desc_configuration[] =
 {
-  // Config: self-powered with remote wakeup support, max power up to 100 mA
+  // Inteface count, string index, total length, attribute, power in mA
   TUD_CONFIG_DESCRIPTOR(ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100),
 
-#if CFG_TUD_MSC
-  TUD_MSC_DESCRIPTOR(ITF_NUM_MSC, 5, EPNUM_MSC, 0x80 | EPNUM_MSC, 64), // highspeed 512
-#endif
+  // Interface number, string index, EP Out & EP In address, EP size
+  TUD_MSC_DESCRIPTOR(ITF_NUM_MSC, 0, EPNUM_MSC, 0x80 | EPNUM_MSC, 64), // highspeed 512
 };
 
 //------------- String Descriptors -------------//

+ 65 - 0
examples/make.mk

@@ -0,0 +1,65 @@
+#
+# Common make definition for all examples
+#
+
+# Compiler 
+CROSS_COMPILE = arm-none-eabi-
+CC = $(CROSS_COMPILE)gcc
+CXX = $(CROSS_COMPILE)g++
+OBJCOPY = $(CROSS_COMPILE)objcopy
+SIZE = $(CROSS_COMPILE)size
+MKDIR = mkdir
+SED = sed
+CP = cp
+RM = rm
+
+# Select the board to build for.
+ifeq ($(BOARD),)
+  $(info You must provide a BOARD parameter with 'BOARD=')
+  $(info Possible values are:)
+  $(info $(sort $(subst /.,,$(subst $(TOP)/hw/bsp/,,$(wildcard $(TOP)/hw/bsp/*/.)))))
+  $(error BOARD not defined)
+else
+  ifeq ($(wildcard $(TOP)/hw/bsp/$(BOARD)/.),)
+    $(error Invalid BOARD specified)
+  endif
+endif
+
+# Build directory
+BUILD = build-$(BOARD)
+
+# Board specific
+include $(TOP)/hw/bsp/$(BOARD)/board.mk
+
+# Compiler Flags
+CFLAGS += \
+	-fsingle-precision-constant \
+	-fno-strict-aliasing \
+	-Wdouble-promotion \
+	-Wno-endif-labels \
+	-Wstrict-prototypes \
+	-Wall \
+	-Werror \
+	-Werror-implicit-function-declaration \
+	-Wfloat-equal \
+	-Wundef \
+	-Wshadow \
+	-Wwrite-strings \
+	-Wsign-compare \
+	-Wmissing-format-attribute \
+	-Wno-deprecated-declarations \
+	-Wnested-externs \
+	-Wunreachable-code \
+	-Wno-error=lto-type-mismatch \
+	-ffunction-sections \
+	-fdata-sections
+
+# This causes lots of warning with nrf5x build due to nrfx code
+# CFLAGS += -Wcast-align
+
+# Debugging/Optimization
+ifeq ($(DEBUG), 1)
+  CFLAGS += -O0 -ggdb
+else
+  CFLAGS += -flto -Os
+endif

+ 111 - 0
examples/rules.mk

@@ -0,0 +1,111 @@
+#
+# Common make definition for all examples
+#
+
+# libc
+LIBS = -lgcc -lc -lm -lnosys
+
+# TinyUSB Stack source
+SRC_C += \
+	src/common/tusb_fifo.c \
+	src/device/usbd.c \
+	src/device/usbd_control.c \
+	src/class/msc/msc_device.c \
+	src/class/cdc/cdc_device.c \
+	src/class/hid/hid_device.c \
+	src/tusb.c \
+	src/portable/$(VENDOR)/$(CHIP_FAMILY)/dcd_$(CHIP_FAMILY).c
+
+# TinyUSB stack include
+INC += $(TOP)/src
+
+#
+CFLAGS += $(addprefix -I,$(INC))
+LDFLAGS += $(CFLAGS) -fshort-enums -Wl,-T,$(TOP)/$(LD_FILE) -Wl,-Map=$@.map -Wl,-cref -Wl,-gc-sections -specs=nosys.specs -specs=nano.specs
+
+# Assembly files can be name with upper case .S, convert it to .s 
+SRC_S := $(SRC_S:.S=.s)
+
+# Due to GCC LTO bug https://bugs.launchpad.net/gcc-arm-embedded/+bug/1747966
+# assembly file should be placed first in linking order
+OBJ += $(addprefix $(BUILD)/obj/, $(SRC_S:.s=.o))
+OBJ += $(addprefix $(BUILD)/obj/, $(SRC_C:.c=.o))
+
+# Verbose mode
+ifeq ("$(V)","1")
+$(info CFLAGS  $(CFLAGS) ) $(info )
+$(info LDFLAGS $(LDFLAGS)) $(info )
+$(info ASFLAGS $(ASFLAGS)) $(info )
+endif
+
+# Set all as default goal
+.DEFAULT_GOAL := all
+all: $(BUILD)/$(BOARD)-firmware.bin size
+
+OBJ_DIRS = $(sort $(dir $(OBJ)))
+$(OBJ): | $(OBJ_DIRS)
+$(OBJ_DIRS):
+	@$(MKDIR) -p $@
+
+$(BUILD)/$(BOARD)-firmware.elf: $(OBJ)
+	@echo LINK $@
+	@$(CC) -o $@ $(LDFLAGS) $^ -Wl,--start-group $(LIBS) -Wl,--end-group
+
+$(BUILD)/$(BOARD)-firmware.bin: $(BUILD)/$(BOARD)-firmware.elf
+	@echo CREATE $@
+	@$(OBJCOPY) -O binary -j .vectors -j .text -j .data $^ $@
+	
+$(BUILD)/$(BOARD)-firmware.hex: $(BUILD)/$(BOARD)-firmware.elf	
+	@echo CREATE $@
+	@$(OBJCOPY) -O ihex $^ $@
+
+# We set vpath to point to the top of the tree so that the source files
+# can be located. By following this scheme, it allows a single build rule
+# to be used to compile all .c files.
+vpath %.c . $(TOP)
+$(BUILD)/obj/%.o: %.c
+	@echo CC $(notdir $@)
+	@$(CC) $(CFLAGS) -c -MD -o $@ $<
+	@# The following fixes the dependency file.
+	@# See http://make.paulandlesley.org/autodep.html for details.
+	@# Regex adjusted from the above to play better with Windows paths, etc.
+	@$(CP) $(@:.o=.d) $(@:.o=.P); \
+	  $(SED) -e 's/#.*//' -e 's/^.*:  *//' -e 's/ *\\$$//' \
+	      -e '/^$$/ d' -e 's/$$/ :/' < $(@:.o=.d) >> $(@:.o=.P); \
+	  $(RM) $(@:.o=.d)
+
+# ASM sources lower case .s
+vpath %.s . $(TOP)
+$(BUILD)/obj/%.o: %.s
+	@echo AS $(notdir $@)
+	@$(CC) -x assembler-with-cpp $(ASFLAGS) -c -o $@ $<
+
+# ASM sources upper case .S
+vpath %.S . $(TOP)
+$(BUILD)/obj/%.o: %.S
+	@echo AS $(notdir $@)
+	@$(CC) -x assembler-with-cpp $(ASFLAGS) -c -o $@ $<
+
+size: $(BUILD)/$(BOARD)-firmware.elf
+	-@echo ''
+	@$(SIZE) $<
+	-@echo ''
+
+clean:
+	rm -rf build-$(BOARD)
+	
+# Flash binary using Jlink
+ifeq ($(OS),Windows_NT)
+  JLINKEXE = JLink.exe
+else 
+  JLINKEXE = JLinkExe
+endif
+
+# Flash using jlink
+flash-jlink: $(BUILD)/$(BOARD)-firmware.hex
+	@echo halt > $(BUILD)/$(BOARD).jlink
+	@echo loadfile $^ >> $(BUILD)/$(BOARD).jlink
+	@echo r >> $(BUILD)/$(BOARD).jlink
+	@echo go >> $(BUILD)/$(BOARD).jlink
+	@echo exit >> $(BUILD)/$(BOARD).jlink
+	$(JLINKEXE) -device $(JLINK_DEVICE) -if $(JLINK_IF) -speed auto -CommandFile $(BUILD)/$(BOARD).jlink

+ 54 - 0
hw/bsp/feather_nrf52840_express/board.mk

@@ -0,0 +1,54 @@
+CFLAGS += \
+	-mthumb \
+	-mabi=aapcs \
+	-mcpu=cortex-m4 \
+	-mfloat-abi=hard \
+	-mfpu=fpv4-sp-d16 \
+	-DCFG_TUSB_MCU=OPT_MCU_NRF5X \
+	-DNRF52840_XXAA \
+  -DCONFIG_GPIO_AS_PINRESET
+
+# nrfx issue undef _ARMCC_VERSION usage https://github.com/NordicSemiconductor/nrfx/issues/49
+CFLAGS += -Wno-error=undef 
+
+# All source paths should be relative to the top level.
+LD_FILE = hw/mcu/nordic/nrfx/mdk/nrf52840_xxaa.ld
+
+LDFLAGS += -L$(TOP)/hw/mcu/nordic/nrfx/mdk
+
+SRC_C += \
+	hw/mcu/nordic/nrfx/drivers/src/nrfx_power.c \
+	hw/mcu/nordic/nrfx/mdk/system_nrf52840.c \
+
+# TODO remove later
+SRC_C += src/portable/$(VENDOR)/$(CHIP_FAMILY)/hal_$(CHIP_FAMILY).c
+
+INC += \
+	$(TOP)/hw/cmsis/Include \
+	$(TOP)/hw/mcu/nordic \
+	$(TOP)/hw/mcu/nordic/nrfx \
+	$(TOP)/hw/mcu/nordic/nrfx/mdk \
+	$(TOP)/hw/mcu/nordic/nrfx/hal \
+	$(TOP)/hw/mcu/nordic/nrfx/drivers/include \
+	$(TOP)/hw/mcu/nordic/nrfx/drivers/src \
+
+SRC_S += hw/mcu/nordic/nrfx/mdk/gcc_startup_nrf52840.S
+
+ASFLAGS += -D__HEAP_SIZE=0
+ASFLAGS += -DSWI_DISABLE0
+ASFLAGS += -DFLOAT_ABI_HARD
+ASFLAGS += -DNRF52840_XXAA
+
+# For TinyUSB port source
+VENDOR = nordic
+CHIP_FAMILY = nrf5x
+
+# For freeRTOS port source
+FREERTOS_PORT = ARM_CM4F
+
+# For flash-jlink target
+JLINK_DEVICE = nRF52840_xxAA
+JLINK_IF = swd
+
+# flash using jlink
+flash: flash-jlink

+ 197 - 0
hw/bsp/feather_nrf52840_express/board_feather_nrf52840_express.c

@@ -0,0 +1,197 @@
+/* 
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2018, hathach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#include "bsp/board.h"
+
+#include "nrfx.h"
+#include "nrfx/hal/nrf_gpio.h"
+#include "nrfx/drivers/include/nrfx_power.h"
+#include "nrfx/drivers/include/nrfx_qspi.h"
+
+#ifdef SOFTDEVICE_PRESENT
+#include "nrf_sdm.h"
+#include "nrf_soc.h"
+#endif
+
+/*------------------------------------------------------------------*/
+/* MACRO TYPEDEF CONSTANT ENUM
+ *------------------------------------------------------------------*/
+#define _PINNUM(port, pin)    ((port)*32 + (pin))
+
+#define LED_PIN         _PINNUM(1, 15)
+#define LED_STATE_ON    1
+
+#define BUTTON_PIN      _PINNUM(1, 02)
+
+/*------------------------------------------------------------------*/
+/* TUSB HAL MILLISECOND
+ *------------------------------------------------------------------*/
+#if CFG_TUSB_OS == OPT_OS_NONE
+volatile uint32_t system_ticks = 0;
+
+void SysTick_Handler (void)
+{
+  system_ticks++;
+}
+
+uint32_t board_millis(void)
+{
+  return system_ticks;
+}
+
+#endif
+
+/*------------------------------------------------------------------*/
+/* BOARD API
+ *------------------------------------------------------------------*/
+
+// tinyusb function that handles power event (detected, ready, removed)
+// We must call it within SD's SOC event handler, or set it as power event handler if SD is not enabled.
+extern void tusb_hal_nrf_power_event(uint32_t event);
+
+void board_init(void)
+{
+  // Config clock source: XTAL or RC in sdk_config.h
+  NRF_CLOCK->LFCLKSRC = (uint32_t)((CLOCK_LFCLKSRC_SRC_Xtal << CLOCK_LFCLKSRC_SRC_Pos) & CLOCK_LFCLKSRC_SRC_Msk);
+  NRF_CLOCK->TASKS_LFCLKSTART = 1UL;
+
+  // LED
+  nrf_gpio_cfg_output(LED_PIN);
+  board_led_write(false);
+
+  // Button
+  nrf_gpio_cfg_input(BUTTON_PIN, NRF_GPIO_PIN_PULLUP);
+
+#if CFG_TUSB_OS == OPT_OS_NONE
+  // 1ms tick timer
+  SysTick_Config(SystemCoreClock/1000);
+#endif
+
+  // Priorities 0, 1, 4 (nRF52) are reserved for SoftDevice
+  // 2 is highest for application
+  NVIC_SetPriority(USBD_IRQn, 2);
+
+  // USB power may already be ready at this time -> no event generated
+  // We need to invoke the handler based on the status initially
+  uint32_t usb_reg;
+
+#ifdef SOFTDEVICE_PRESENT
+  uint8_t sd_en = false;
+  sd_softdevice_is_enabled(&sd_en);
+
+  if ( sd_en ) {
+    sd_power_usbdetected_enable(true);
+    sd_power_usbpwrrdy_enable(true);
+    sd_power_usbremoved_enable(true);
+
+    sd_power_usbregstatus_get(&usb_reg);
+  }else
+#endif
+  {
+    // Power module init
+    const nrfx_power_config_t pwr_cfg = { 0 };
+    nrfx_power_init(&pwr_cfg);
+
+    // Register tusb function as USB power handler
+    const nrfx_power_usbevt_config_t config = { .handler = (nrfx_power_usb_event_handler_t) tusb_hal_nrf_power_event };
+    nrfx_power_usbevt_init(&config);
+
+    nrfx_power_usbevt_enable();
+
+    usb_reg = NRF_POWER->USBREGSTATUS;
+  }
+
+  if ( usb_reg & POWER_USBREGSTATUS_VBUSDETECT_Msk ) tusb_hal_nrf_power_event(NRFX_POWER_USB_EVT_DETECTED);
+  if ( usb_reg & POWER_USBREGSTATUS_OUTPUTRDY_Msk  ) tusb_hal_nrf_power_event(NRFX_POWER_USB_EVT_READY);
+}
+
+void board_led_write(bool state)
+{
+  nrf_gpio_pin_write(LED_PIN, state ? LED_STATE_ON : (1-LED_STATE_ON));
+}
+
+uint32_t board_button_read(void)
+{
+  // button is active LOW
+  return (nrf_gpio_pin_read(BUTTON_PIN) ? 0 : 1);
+}
+
+int board_uart_read(uint8_t* buf, int len)
+{
+  (void) buf;
+  (void) len;
+  return 0;
+}
+
+int board_uart_write(void const * buf, int len)
+{
+  (void) buf;
+  (void) len;
+  return 0;
+}
+
+#ifdef SOFTDEVICE_PRESENT
+// process SOC event from SD
+uint32_t proc_soc(void)
+{
+  uint32_t soc_evt;
+  uint32_t err = sd_evt_get(&soc_evt);
+
+  if (NRF_SUCCESS == err)
+  {
+    /*------------- usb power event handler -------------*/
+    int32_t usbevt = (soc_evt == NRF_EVT_POWER_USB_DETECTED   ) ? NRFX_POWER_USB_EVT_DETECTED:
+                     (soc_evt == NRF_EVT_POWER_USB_POWER_READY) ? NRFX_POWER_USB_EVT_READY   :
+                     (soc_evt == NRF_EVT_POWER_USB_REMOVED    ) ? NRFX_POWER_USB_EVT_REMOVED : -1;
+
+    if ( usbevt >= 0) tusb_hal_nrf_power_event(usbevt);
+  }
+
+  return err;
+}
+
+uint32_t proc_ble(void)
+{
+  // do nothing with ble
+  return NRF_ERROR_NOT_FOUND;
+}
+
+void SD_EVT_IRQHandler(void)
+{
+  // process BLE and SOC until there is no more events
+  while( (NRF_ERROR_NOT_FOUND != proc_ble()) || (NRF_ERROR_NOT_FOUND != proc_soc()) )
+  {
+
+  }
+}
+
+void nrf_error_cb(uint32_t id, uint32_t pc, uint32_t info)
+{
+  (void) id;
+  (void) pc;
+  (void) info;
+}
+#endif

+ 15 - 9
hw/bsp/metro_m0_express/board.mk

@@ -23,20 +23,26 @@ SRC_C += \
 	hw/mcu/microchip/samd/asf4/samd21/hal/src/hal_atomic.c
 
 INC += \
-	-I$(TOP)/hw/mcu/microchip/samd/asf4/samd21/ \
-	-I$(TOP)/hw/mcu/microchip/samd/asf4/samd21/config \
-	-I$(TOP)/hw/mcu/microchip/samd/asf4/samd21/include \
-	-I$(TOP)/hw/mcu/microchip/samd/asf4/samd21/hal/include \
-	-I$(TOP)/hw/mcu/microchip/samd/asf4/samd21/hal/utils/include \
-	-I$(TOP)/hw/mcu/microchip/samd/asf4/samd51/hpl/pm/ \
-	-I$(TOP)/hw/mcu/microchip/samd/asf4/samd21/hpl/port \
-	-I$(TOP)/hw/mcu/microchip/samd/asf4/samd21/hri \
-	-I$(TOP)/hw/mcu/microchip/samd/asf4/samd21/CMSIS/Include
+	$(TOP)/hw/mcu/microchip/samd/asf4/samd21/ \
+	$(TOP)/hw/mcu/microchip/samd/asf4/samd21/config \
+	$(TOP)/hw/mcu/microchip/samd/asf4/samd21/include \
+	$(TOP)/hw/mcu/microchip/samd/asf4/samd21/hal/include \
+	$(TOP)/hw/mcu/microchip/samd/asf4/samd21/hal/utils/include \
+	$(TOP)/hw/mcu/microchip/samd/asf4/samd51/hpl/pm/ \
+	$(TOP)/hw/mcu/microchip/samd/asf4/samd21/hpl/port \
+	$(TOP)/hw/mcu/microchip/samd/asf4/samd21/hri \
+	$(TOP)/hw/mcu/microchip/samd/asf4/samd21/CMSIS/Include
 
+# For TinyUSB port source 
 VENDOR = microchip
 CHIP_FAMILY = samd21
 
+# For freeRTOS port source
+FREERTOS_PORT = ARM_CM0
+
+# For flash-jlink target
 JLINK_DEVICE = ATSAMD21G18
+JLINK_IF = swd
 
 # flash using jlink
 flash: flash-jlink

+ 15 - 9
hw/bsp/metro_m4_express/board.mk

@@ -25,19 +25,25 @@ SRC_C += \
 	hw/mcu/microchip/samd/asf4/samd51/hal/src/hal_atomic.c
 
 INC += \
-	-I$(TOP)/hw/mcu/microchip/samd/asf4/samd51/ \
-	-I$(TOP)/hw/mcu/microchip/samd/asf4/samd51/config \
-	-I$(TOP)/hw/mcu/microchip/samd/asf4/samd51/include \
-	-I$(TOP)/hw/mcu/microchip/samd/asf4/samd51/hal/include \
-	-I$(TOP)/hw/mcu/microchip/samd/asf4/samd51/hal/utils/include \
-	-I$(TOP)/hw/mcu/microchip/samd/asf4/samd51/hpl/port \
-	-I$(TOP)/hw/mcu/microchip/samd/asf4/samd51/hri \
-	-I$(TOP)/hw/mcu/microchip/samd/asf4/samd51/CMSIS/Include
-
+	$(TOP)/hw/mcu/microchip/samd/asf4/samd51/ \
+	$(TOP)/hw/mcu/microchip/samd/asf4/samd51/config \
+	$(TOP)/hw/mcu/microchip/samd/asf4/samd51/include \
+	$(TOP)/hw/mcu/microchip/samd/asf4/samd51/hal/include \
+	$(TOP)/hw/mcu/microchip/samd/asf4/samd51/hal/utils/include \
+	$(TOP)/hw/mcu/microchip/samd/asf4/samd51/hpl/port \
+	$(TOP)/hw/mcu/microchip/samd/asf4/samd51/hri \
+	$(TOP)/hw/mcu/microchip/samd/asf4/samd51/CMSIS/Include
+
+# For TinyUSB port source
 VENDOR = microchip
 CHIP_FAMILY = samd51
 
+# For freeRTOS port source
+FREERTOS_PORT = ARM_CM4F
+
+# For flash-jlink target
 JLINK_DEVICE = ATSAMD51J19
+JLINK_IF = swd
 
 # flash using jlink
 flash: flash-jlink

+ 17 - 12
hw/bsp/pca10056/board.mk

@@ -1,11 +1,12 @@
 CFLAGS += \
-	-DCFG_TUSB_MCU=OPT_MCU_NRF5X \
-	-DNRF52840_XXAA \
 	-mthumb \
 	-mabi=aapcs \
 	-mcpu=cortex-m4 \
 	-mfloat-abi=hard \
-	-mfpu=fpv4-sp-d16
+	-mfpu=fpv4-sp-d16 \
+	-DCFG_TUSB_MCU=OPT_MCU_NRF5X \
+	-DNRF52840_XXAA \
+  -DCONFIG_GPIO_AS_PINRESET
 
 # nrfx issue undef _ARMCC_VERSION usage https://github.com/NordicSemiconductor/nrfx/issues/49
 CFLAGS += -Wno-error=undef 
@@ -23,27 +24,31 @@ SRC_C += \
 SRC_C += src/portable/$(VENDOR)/$(CHIP_FAMILY)/hal_$(CHIP_FAMILY).c
 
 INC += \
-	-I$(TOP)/hw/cmsis/Include \
-	-I$(TOP)/hw/mcu/nordic \
-	-I$(TOP)/hw/mcu/nordic/nrfx \
-	-I$(TOP)/hw/mcu/nordic/nrfx/mdk \
-	-I$(TOP)/hw/mcu/nordic/nrfx/hal \
-	-I$(TOP)/hw/mcu/nordic/nrfx/drivers/include \
-	-I$(TOP)/hw/mcu/nordic/nrfx/drivers/src \
+	$(TOP)/hw/cmsis/Include \
+	$(TOP)/hw/mcu/nordic \
+	$(TOP)/hw/mcu/nordic/nrfx \
+	$(TOP)/hw/mcu/nordic/nrfx/mdk \
+	$(TOP)/hw/mcu/nordic/nrfx/hal \
+	$(TOP)/hw/mcu/nordic/nrfx/drivers/include \
+	$(TOP)/hw/mcu/nordic/nrfx/drivers/src \
 
 SRC_S += hw/mcu/nordic/nrfx/mdk/gcc_startup_nrf52840.S
 
 ASFLAGS += -D__HEAP_SIZE=0
-ASFLAGS += -DCONFIG_GPIO_AS_PINRESET
-ASFLAGS += -DBLE_STACK_SUPPORT_REQD
 ASFLAGS += -DSWI_DISABLE0
 ASFLAGS += -DFLOAT_ABI_HARD
 ASFLAGS += -DNRF52840_XXAA
 
+# For TinyUSB port source
 VENDOR = nordic
 CHIP_FAMILY = nrf5x
 
+# For freeRTOS port source
+FREERTOS_PORT = ARM_CM4F
+
+# For flash-jlink target
 JLINK_DEVICE = nRF52840_xxAA
+JLINK_IF = swd
 
 # flash using jlink
 flash: flash-jlink

+ 10 - 4
hw/bsp/stm32f303disc/board.mk

@@ -25,15 +25,21 @@ SRC_S += \
 	hw/mcu/st/startup/stm32f3/startup_stm32f303xc.s
 
 INC += \
-	-I$(TOP)/hw/bsp/stm32f303disc \
- 	-I$(TOP)/hw/mcu/st/cmsis \
-	-I$(TOP)/hw/mcu/st/stm32lib/CMSIS/STM32F3xx/Include \
-	-I$(TOP)/hw/mcu/st/stm32lib/STM32F3xx_HAL_Driver/Inc
+	$(TOP)/hw/bsp/stm32f303disc \
+	$(TOP)/hw/mcu/st/cmsis \
+	$(TOP)/hw/mcu/st/stm32lib/CMSIS/STM32F3xx/Include \
+	$(TOP)/hw/mcu/st/stm32lib/STM32F3xx_HAL_Driver/Inc
 
+# For TinyUSB port source
 VENDOR = st
 CHIP_FAMILY = stm32f3
 
+# For freeRTOS port source
+FREERTOS_PORT = ARM_CM4F
+
+# For flash-jlink target
 JLINK_DEVICE = stm32f303vc
+JLINK_IF = swd
 
 # Path to STM32 Cube Programmer CLI, should be added into system path 
 STM32Prog = STM32_Programmer_CLI

+ 8 - 2
hw/bsp/stm32f407g_disc1/board.mk

@@ -21,13 +21,19 @@ SRC_S += \
 	hw/mcu/st/startup/stm32f4/startup_stm32f407xx.s
 
 INC += \
-	-I$(TOP)/hw/mcu/st/stm32lib/CMSIS/STM32F4xx/Include \
-	-I$(TOP)/hw/mcu/st/cmsis
+	$(TOP)/hw/mcu/st/stm32lib/CMSIS/STM32F4xx/Include \
+	$(TOP)/hw/mcu/st/cmsis
 
+# For TinyUSB port source
 VENDOR = st
 CHIP_FAMILY = stm32f4
 
+# For freeRTOS port source
+FREERTOS_PORT = ARM_CM4F
+
+# For flash-jlink target
 JLINK_DEVICE = stm32f407vg
+JLINK_IF = swd
 
 # Path to STM32 Cube Programmer CLI, should be added into system path 
 STM32Prog = STM32_Programmer_CLI

+ 1 - 1
examples/device/cdc_msc_hid_freertos/ses/nrf5x/FreeRTOSConfig.h → hw/mcu/nordic/FreeRTOSConfig.h

@@ -69,7 +69,7 @@
 /* Hook function related definitions. */
 #define configUSE_IDLE_HOOK                    0
 #define configUSE_TICK_HOOK                    0
-#define configUSE_MALLOC_FAILED_HOOK           1
+#define configUSE_MALLOC_FAILED_HOOK           0 // cause nested extern warning
 #define configCHECK_FOR_STACK_OVERFLOW         2
 
 /* Run time and task stats gathering related definitions. */

+ 1 - 1
hw/mcu/nordic/nrfx

@@ -1 +1 @@
-Subproject commit 6f54f689e9555ea18f9aca87caf44a3419e5dd7a
+Subproject commit 9d68726e41c321f1772c187bd12d82f5b13104f1

+ 4 - 6
src/class/cdc/cdc_device.c

@@ -71,7 +71,7 @@ typedef struct
 //--------------------------------------------------------------------+
 // INTERNAL OBJECT & FUNCTION DECLARATION
 //--------------------------------------------------------------------+
-CFG_TUSB_MEM_SECTION static cdcd_interface_t _cdcd_itf[CFG_TUD_CDC] = { { 0 } };
+CFG_TUSB_MEM_SECTION static cdcd_interface_t _cdcd_itf[CFG_TUD_CDC];
 
 // TODO will be replaced by dcd_edpt_busy()
 bool pending_read_from_host;
@@ -289,14 +289,12 @@ bool cdcd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t
        (TUSB_CLASS_CDC_DATA == ((tusb_desc_interface_t const *) p_desc)->bInterfaceClass) )
   {
     // next to endpoint descriptor
-    (*p_length) += tu_desc_len(p_desc);
     p_desc = tu_desc_next(p_desc);
 
-    // Open endpoint pair with usbd helper
-    tusb_desc_endpoint_t const *p_desc_ep = (tusb_desc_endpoint_t const *) p_desc;
-    TU_ASSERT( usbd_open_edpt_pair(rhport, p_desc_ep, TUSB_XFER_BULK, &p_cdc->ep_out, &p_cdc->ep_in) );
+    // Open endpoint pair
+    TU_ASSERT( usbd_open_edpt_pair(rhport, p_desc, 2, TUSB_XFER_BULK, &p_cdc->ep_out, &p_cdc->ep_in) );
 
-    (*p_length) += 2*sizeof(tusb_desc_endpoint_t);
+    (*p_length) += sizeof(tusb_desc_interface_t) + 2*sizeof(tusb_desc_endpoint_t);
   }
 
   // Prepare for incoming data

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

@@ -89,9 +89,17 @@ static inline bool     tud_cdc_write_flush     (void)
 //--------------------------------------------------------------------+
 // APPLICATION CALLBACK API (WEAK is optional)
 //--------------------------------------------------------------------+
+
+// Invoked when received new data
 ATTR_WEAK void tud_cdc_rx_cb(uint8_t itf);
+
+// Invoked when received `wanted_char`
 ATTR_WEAK void tud_cdc_rx_wanted_cb(uint8_t itf, char wanted_char);
+
+// Invoked when line state DTR & RTS are changed via SET_CONTROL_LINE_STATE
 ATTR_WEAK void tud_cdc_line_state_cb(uint8_t itf, bool dtr, bool rts);
+
+// Invoked when line coding is change via SET_LINE_CODING
 ATTR_WEAK void tud_cdc_line_coding_cb(uint8_t itf, cdc_line_coding_t const* p_line_coding);
 
 /** @} */

+ 56 - 55
src/class/hid/hid.h

@@ -43,6 +43,20 @@
 /** \defgroup ClassDriver_HID_Common Common Definitions
  *  @{ */
 
+ /// USB HID Descriptor
+typedef struct ATTR_PACKED
+{
+  uint8_t  bLength;         /**< Numeric expression that is the total size of the HID descriptor */
+  uint8_t  bDescriptorType; /**< Constant name specifying type of HID descriptor. */
+
+  uint16_t bcdHID;          /**< Numeric expression identifying the HID Class Specification release */
+  uint8_t  bCountryCode;    /**< Numeric expression identifying country code of the localized hardware.  */
+  uint8_t  bNumDescriptors; /**< Numeric expression specifying the number of class descriptors */
+
+  uint8_t  bReportType;     /**< Type of HID class report. */
+  uint16_t wReportLength;   /**< the total size of the Report descriptor. */
+} tusb_hid_descriptor_hid_t;
+
 /// HID Subclass
 typedef enum
 {
@@ -69,9 +83,10 @@ typedef enum
 /// HID Request Report Type
 typedef enum
 {
-  HID_REPORT_TYPE_INPUT = 1, ///< Input
-  HID_REPORT_TYPE_OUTPUT,    ///< Output
-  HID_REPORT_TYPE_FEATURE    ///< Feature
+  HID_REPORT_TYPE_INVALID = 0,
+  HID_REPORT_TYPE_INPUT,      ///< Input
+  HID_REPORT_TYPE_OUTPUT,     ///< Output
+  HID_REPORT_TYPE_FEATURE     ///< Feature
 }hid_report_type_t;
 
 /// HID Class Specific Control Request
@@ -85,59 +100,45 @@ typedef enum
   HID_REQ_CONTROL_SET_PROTOCOL = 0x0b  ///< Set Protocol
 }hid_request_type_t;
 
-/// USB HID Descriptor
-typedef struct ATTR_PACKED
-{
-  uint8_t  bLength;         /**< Numeric expression that is the total size of the HID descriptor */
-  uint8_t  bDescriptorType; /**< Constant name specifying type of HID descriptor. */
-
-  uint16_t bcdHID;          /**< Numeric expression identifying the HID Class Specification release */
-  uint8_t  bCountryCode;    /**< Numeric expression identifying country code of the localized hardware.  */
-  uint8_t  bNumDescriptors; /**< Numeric expression specifying the number of class descriptors */
-
-  uint8_t  bReportType;     /**< Type of HID class report. */
-  uint16_t wReportLength;   /**< the total size of the Report descriptor. */
-} tusb_hid_descriptor_hid_t;
-
 /// HID Country Code
 typedef enum
 {
-  HID_Local_NotSupported = 0   , ///< NotSupported
-  HID_Local_Arabic             , ///< Arabic
-  HID_Local_Belgian            , ///< Belgian
-  HID_Local_Canadian_Bilingual , ///< Canadian_Bilingual
-  HID_Local_Canadian_French    , ///< Canadian_French
-  HID_Local_Czech_Republic     , ///< Czech_Republic
-  HID_Local_Danish             , ///< Danish
-  HID_Local_Finnish            , ///< Finnish
-  HID_Local_French             , ///< French
-  HID_Local_German             , ///< German
-  HID_Local_Greek              , ///< Greek
-  HID_Local_Hebrew             , ///< Hebrew
-  HID_Local_Hungary            , ///< Hungary
-  HID_Local_International      , ///< International
-  HID_Local_Italian            , ///< Italian
-  HID_Local_Japan_Katakana     , ///< Japan_Katakana
-  HID_Local_Korean             , ///< Korean
-  HID_Local_Latin_American     , ///< Latin_American
-  HID_Local_Netherlands_Dutch  , ///< Netherlands/Dutch
-  HID_Local_Norwegian          , ///< Norwegian
-  HID_Local_Persian_Farsi      , ///< Persian (Farsi)
-  HID_Local_Poland             , ///< Poland
-  HID_Local_Portuguese         , ///< Portuguese
-  HID_Local_Russia             , ///< Russia
-  HID_Local_Slovakia           , ///< Slovakia
-  HID_Local_Spanish            , ///< Spanish
-  HID_Local_Swedish            , ///< Swedish
-  HID_Local_Swiss_French       , ///< Swiss/French
-  HID_Local_Swiss_German       , ///< Swiss/German
-  HID_Local_Switzerland        , ///< Switzerland
-  HID_Local_Taiwan             , ///< Taiwan
-  HID_Local_Turkish_Q          , ///< Turkish-Q
-  HID_Local_UK                 , ///< UK
-  HID_Local_US                 , ///< US
-  HID_Local_Yugoslavia         , ///< Yugoslavia
-  HID_Local_Turkish_F            ///< Turkish-F
+  HID_LOCAL_NotSupported = 0   , ///< NotSupported
+  HID_LOCAL_Arabic             , ///< Arabic
+  HID_LOCAL_Belgian            , ///< Belgian
+  HID_LOCAL_Canadian_Bilingual , ///< Canadian_Bilingual
+  HID_LOCAL_Canadian_French    , ///< Canadian_French
+  HID_LOCAL_Czech_Republic     , ///< Czech_Republic
+  HID_LOCAL_Danish             , ///< Danish
+  HID_LOCAL_Finnish            , ///< Finnish
+  HID_LOCAL_French             , ///< French
+  HID_LOCAL_German             , ///< German
+  HID_LOCAL_Greek              , ///< Greek
+  HID_LOCAL_Hebrew             , ///< Hebrew
+  HID_LOCAL_Hungary            , ///< Hungary
+  HID_LOCAL_International      , ///< International
+  HID_LOCAL_Italian            , ///< Italian
+  HID_LOCAL_Japan_Katakana     , ///< Japan_Katakana
+  HID_LOCAL_Korean             , ///< Korean
+  HID_LOCAL_Latin_American     , ///< Latin_American
+  HID_LOCAL_Netherlands_Dutch  , ///< Netherlands/Dutch
+  HID_LOCAL_Norwegian          , ///< Norwegian
+  HID_LOCAL_Persian_Farsi      , ///< Persian (Farsi)
+  HID_LOCAL_Poland             , ///< Poland
+  HID_LOCAL_Portuguese         , ///< Portuguese
+  HID_LOCAL_Russia             , ///< Russia
+  HID_LOCAL_Slovakia           , ///< Slovakia
+  HID_LOCAL_Spanish            , ///< Spanish
+  HID_LOCAL_Swedish            , ///< Swedish
+  HID_LOCAL_Swiss_French       , ///< Swiss/French
+  HID_LOCAL_Swiss_German       , ///< Swiss/German
+  HID_LOCAL_Switzerland        , ///< Switzerland
+  HID_LOCAL_Taiwan             , ///< Taiwan
+  HID_LOCAL_Turkish_Q          , ///< Turkish-Q
+  HID_LOCAL_UK                 , ///< UK
+  HID_LOCAL_US                 , ///< US
+  HID_LOCAL_Yugoslavia         , ///< Yugoslavia
+  HID_LOCAL_Turkish_F            ///< Turkish-F
 } hid_country_code_t;
 
 /** @} */
@@ -155,7 +156,7 @@ typedef struct ATTR_PACKED
   int8_t  x;       /**< Current delta x movement of the mouse. */
   int8_t  y;       /**< Current delta y movement on the mouse. */
   int8_t  wheel;   /**< Current delta wheel movement on the mouse. */
-//  int8_t  pan;
+  int8_t  pan;     // using AC Pan
 } hid_mouse_report_t;
 
 /// Standard Mouse Buttons Bitmap
@@ -461,7 +462,7 @@ enum {
   HID_USAGE_PAGE_MSR             = 0x8e,
   HID_USAGE_PAGE_CAMERA          = 0x90,
   HID_USAGE_PAGE_ARCADE          = 0x91,
-  HID_USAGE_PAGE_VENDOR          = 0xFFFF // 0xFF00 - 0xFFFF
+  HID_USAGE_PAGE_VENDOR          = 0xFF00 // 0xFF00 - 0xFFFF
 };
 
 /// HID Usage Table - Table 6: Generic Desktop Page

+ 45 - 61
src/class/hid/hid_device.c

@@ -38,24 +38,19 @@
 //--------------------------------------------------------------------+
 // MACRO CONSTANT TYPEDEF
 //--------------------------------------------------------------------+
-
-#ifndef CFG_TUD_HID_BUFSIZE
-#define CFG_TUD_HID_BUFSIZE     16
-#endif
-
 typedef struct
 {
   uint8_t itf_num;
   uint8_t ep_in;
+  uint8_t ep_out;        // optional Out endpoint
   uint8_t boot_protocol; // Boot mouse or keyboard
-  bool    boot_mode;
-
+  bool    boot_mode;     // default = false (Report)
+  uint8_t idle_rate;     // up to application to handle idle rate
   uint16_t reprot_desc_len;
-  uint8_t idle_rate;     // Idle Rate = 0 : only send report if there is changes, i.e skip duplication
-                         // Idle Rate > 0 : skip duplication, but send at least 1 report every idle rate (in unit of 4 ms).
-  uint8_t mouse_button;  // caching button for using with tud_hid_mouse_ API
 
-  CFG_TUSB_MEM_ALIGN uint8_t report_buf[CFG_TUD_HID_BUFSIZE];
+  CFG_TUSB_MEM_ALIGN uint8_t epin_buf[CFG_TUD_HID_BUFSIZE];
+  CFG_TUSB_MEM_ALIGN uint8_t epout_buf[CFG_TUD_HID_BUFSIZE];
+
 }hidd_interface_t;
 
 CFG_TUSB_MEM_SECTION static hidd_interface_t _hidd_itf[CFG_TUD_HID];
@@ -91,15 +86,15 @@ bool tud_hid_report(uint8_t report_id, void const* report, uint8_t len)
   // If report id = 0, skip ID field
   if (report_id)
   {
-    p_hid->report_buf[0] = report_id;
-    memcpy(p_hid->report_buf+1, report, len);
+    p_hid->epin_buf[0] = report_id;
+    memcpy(p_hid->epin_buf+1, report, len);
+    len++;
   }else
   {
-    memcpy(p_hid->report_buf, report, len);
+    memcpy(p_hid->epin_buf, report, len);
   }
 
-  // TODO skip duplication ? and or idle rate
-  return dcd_edpt_xfer(TUD_OPT_RHPORT, p_hid->ep_in, p_hid->report_buf, len + (report_id ? 1 : 0) );
+  return dcd_edpt_xfer(TUD_OPT_RHPORT, p_hid->ep_in, p_hid->epin_buf, len);
 }
 
 bool tud_hid_boot_mode(void)
@@ -125,47 +120,26 @@ bool tud_hid_keyboard_report(uint8_t report_id, uint8_t modifier, uint8_t keycod
     tu_memclr(report.keycode, 6);
   }
 
-  // TODO skip duplication ? and or idle rate
   return tud_hid_report(report_id, &report, sizeof(report));
 }
 
 //--------------------------------------------------------------------+
 // MOUSE APPLICATION API
 //--------------------------------------------------------------------+
-bool tud_hid_mouse_report(uint8_t report_id, uint8_t buttons, int8_t x, int8_t y, int8_t scroll, int8_t pan)
+bool tud_hid_mouse_report(uint8_t report_id, uint8_t buttons, int8_t x, int8_t y, int8_t vertical, int8_t horizontal)
 {
-  (void) pan;
   hid_mouse_report_t report =
   {
     .buttons = buttons,
     .x       = x,
     .y       = y,
-    .wheel   = scroll,
-    //.pan     = pan
+    .wheel   = vertical,
+    .pan     = horizontal
   };
 
-  uint8_t itf = 0;
-  _hidd_itf[itf].mouse_button = buttons;
-
   return tud_hid_report(report_id, &report, sizeof(report));
 }
 
-bool tud_hid_mouse_move(uint8_t report_id, int8_t x, int8_t y)
-{
-  uint8_t itf = 0;
-  uint8_t const button = _hidd_itf[itf].mouse_button;
-
-  return tud_hid_mouse_report(report_id, button, x, y, 0, 0);
-}
-
-bool tud_hid_mouse_scroll(uint8_t report_id, int8_t scroll, int8_t pan)
-{
-  uint8_t itf = 0;
-  uint8_t const button = _hidd_itf[itf].mouse_button;
-
-  return tud_hid_mouse_report(report_id, button, 0, 0, scroll, pan);
-}
-
 //--------------------------------------------------------------------+
 // USBD-CLASS API
 //--------------------------------------------------------------------+
@@ -184,6 +158,10 @@ bool hidd_open(uint8_t rhport, tusb_desc_interface_t const * desc_itf, uint16_t
 {
   uint8_t const *p_desc = (uint8_t const *) desc_itf;
 
+  // TODO support multiple HID interface
+  uint8_t const itf = 0;
+  hidd_interface_t * p_hid = &_hidd_itf[itf];
+
   //------------- HID descriptor -------------//
   p_desc = tu_desc_next(p_desc);
   tusb_hid_descriptor_hid_t const *desc_hid = (tusb_hid_descriptor_hid_t const *) p_desc;
@@ -191,24 +169,19 @@ bool hidd_open(uint8_t rhport, tusb_desc_interface_t const * desc_itf, uint16_t
 
   //------------- Endpoint Descriptor -------------//
   p_desc = tu_desc_next(p_desc);
-  tusb_desc_endpoint_t const *desc_edpt = (tusb_desc_endpoint_t const *) p_desc;
-  TU_ASSERT(TUSB_DESC_ENDPOINT == desc_edpt->bDescriptorType);
-
-  TU_ASSERT(dcd_edpt_open(rhport, desc_edpt));
-
-  // TODO support multiple HID interface
-  uint8_t itf = 0;
-  hidd_interface_t * p_hid = &_hidd_itf[itf];
+  TU_ASSERT(usbd_open_edpt_pair(rhport, p_desc, desc_itf->bNumEndpoints, TUSB_XFER_INTERRUPT, &p_hid->ep_out, &p_hid->ep_in));
 
   if ( desc_itf->bInterfaceSubClass == HID_SUBCLASS_BOOT ) p_hid->boot_protocol = desc_itf->bInterfaceProtocol;
 
   p_hid->boot_mode = false; // default mode is REPORT
   p_hid->itf_num   = desc_itf->bInterfaceNumber;
-  p_hid->ep_in     = desc_edpt->bEndpointAddress;
   p_hid->reprot_desc_len  = desc_hid->wReportLength;
 
   *p_len = sizeof(tusb_desc_interface_t) + sizeof(tusb_hid_descriptor_hid_t) + desc_itf->bNumEndpoints*sizeof(tusb_desc_endpoint_t);
 
+  // Prepare for output endpoint
+  if (p_hid->ep_out) TU_ASSERT(dcd_edpt_xfer(rhport, p_hid->ep_out, p_hid->epout_buf, sizeof(p_hid->epout_buf)));
+
   return true;
 }
 
@@ -245,20 +218,25 @@ bool hidd_control_request(uint8_t rhport, tusb_control_request_t const * p_reque
         uint8_t const report_type = tu_u16_high(p_request->wValue);
         uint8_t const report_id   = tu_u16_low(p_request->wValue);
 
-        uint16_t xferlen  = tud_hid_get_report_cb(report_id, (hid_report_type_t) report_type, p_hid->report_buf, p_request->wLength);
+        uint16_t xferlen  = tud_hid_get_report_cb(report_id, (hid_report_type_t) report_type, p_hid->epin_buf, p_request->wLength);
         TU_ASSERT( xferlen > 0 );
 
-        usbd_control_xfer(rhport, p_request, p_hid->report_buf, xferlen);
+        usbd_control_xfer(rhport, p_request, p_hid->epin_buf, xferlen);
       }
       break;
 
       case  HID_REQ_CONTROL_SET_REPORT:
-        usbd_control_xfer(rhport, p_request, p_hid->report_buf, p_request->wLength);
+        usbd_control_xfer(rhport, p_request, p_hid->epout_buf, p_request->wLength);
       break;
 
       case HID_REQ_CONTROL_SET_IDLE:
-        // TODO idle rate of report
         p_hid->idle_rate = tu_u16_high(p_request->wValue);
+        if ( tud_hid_set_idle_cb )
+        {
+          // stall request if callback return false
+          if ( !tud_hid_set_idle_cb(p_hid->idle_rate) ) return false;
+        }
+
         usbd_control_status(rhport, p_request);
       break;
 
@@ -277,7 +255,7 @@ bool hidd_control_request(uint8_t rhport, tusb_control_request_t const * p_reque
       case HID_REQ_CONTROL_SET_PROTOCOL:
         p_hid->boot_mode = 1 - p_request->wValue; // 0 is Boot, 1 is Report protocol
 
-        if (tud_hid_mode_changed_cb) tud_hid_mode_changed_cb(p_hid->boot_mode);
+        if (tud_hid_boot_mode_cb) tud_hid_boot_mode_cb(p_hid->boot_mode);
 
         usbd_control_status(rhport, p_request);
       break;
@@ -307,19 +285,25 @@ bool hidd_control_request_complete(uint8_t rhport, tusb_control_request_t const
     uint8_t const report_type = tu_u16_high(p_request->wValue);
     uint8_t const report_id   = tu_u16_low(p_request->wValue);
 
-    tud_hid_set_report_cb(report_id, (hid_report_type_t) report_type, p_hid->report_buf, p_request->wLength);
+    tud_hid_set_report_cb(report_id, (hid_report_type_t) report_type, p_hid->epout_buf, p_request->wLength);
   }
 
   return true;
 }
 
-bool hidd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes)
+bool hidd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)
 {
-  // nothing to do
-  (void) rhport;
-  (void) ep_addr;
-  (void) event;
-  (void) xferred_bytes;
+  (void) result;
+
+  // TODO support multiple HID interface
+  uint8_t const itf = 0;
+  hidd_interface_t * p_hid = &_hidd_itf[itf];
+
+  if (ep_addr == p_hid->ep_out)
+  {
+    tud_hid_set_report_cb(0, HID_REPORT_TYPE_INVALID, p_hid->epout_buf, xferred_bytes);
+    TU_ASSERT(dcd_edpt_xfer(rhport, p_hid->ep_out, p_hid->epout_buf, sizeof(p_hid->epout_buf)));
+  }
 
   return true;
 }

+ 97 - 73
src/class/hid/hid_device.h

@@ -39,6 +39,9 @@
 // Class Driver Default Configure & Validation
 //--------------------------------------------------------------------+
 
+#ifndef CFG_TUD_HID_BUFSIZE
+#define CFG_TUD_HID_BUFSIZE     16
+#endif
 
 //--------------------------------------------------------------------+
 // Application API
@@ -53,51 +56,34 @@ bool tud_hid_boot_mode(void);
 // Send report to host
 bool tud_hid_report(uint8_t report_id, void const* report, uint8_t len);
 
-/*------------- Callbacks (Weak is optional) -------------*/
-
-// Invoked when receiving GET_REPORT control request
-// Application must fill buffer report's content and return its length.
-// Return zero will cause the stack to STALL request
-uint16_t tud_hid_get_report_cb(uint8_t report_id, hid_report_type_t report_type, uint8_t* buffer, uint16_t reqlen);
-
-// Invoked when receiving SET_REPORT control request
-void tud_hid_set_report_cb(uint8_t report_id, hid_report_type_t report_type, uint8_t const* buffer, uint16_t bufsize);
-
-// Invoked when host switch mode Boot <-> Report via SET_PROTOCOL request
-void tud_hid_mode_changed_cb(uint8_t boot_mode) ATTR_WEAK;
-
-//--------------------------------------------------------------------+
-// KEYBOARD API
-// Convenient helper to send keyboard report if application use standard/boot
-// layout report as defined by hid_keyboard_report_t
-//--------------------------------------------------------------------+
-
+// KEYBOARD: convenient helper to send keyboard report if application
+// use template layout report as defined by hid_keyboard_report_t
 bool tud_hid_keyboard_report(uint8_t report_id, uint8_t modifier, uint8_t keycode[6]);
 
-static inline bool tud_hid_keyboard_key_release(uint8_t report_id)
-{
-  return tud_hid_keyboard_report(report_id, 0, NULL);
-}
+// MOUSE: convenient helper to send mouse report if application
+// use template layout report as defined by hid_mouse_report_t
+bool tud_hid_mouse_report(uint8_t report_id, uint8_t buttons, int8_t x, int8_t y, int8_t vertical, int8_t horizontal);
 
 //--------------------------------------------------------------------+
-// MOUSE API
-// Convenient helper to send mouse report if application use standard/boot
-// layout report as defined by hid_mouse_report_t
+// Callbacks (Weak is optional)
 //--------------------------------------------------------------------+
 
-bool tud_hid_mouse_report(uint8_t report_id, uint8_t buttons, int8_t x, int8_t y, int8_t scroll, int8_t pan);
-bool tud_hid_mouse_move(uint8_t report_id, int8_t x, int8_t y);
-bool tud_hid_mouse_scroll(uint8_t report_id, int8_t scroll, int8_t pan);
+// Invoked when received GET_REPORT control request
+// Application must fill buffer report's content and return its length.
+// Return zero will cause the stack to STALL request
+uint16_t tud_hid_get_report_cb(uint8_t report_id, hid_report_type_t report_type, uint8_t* buffer, uint16_t reqlen);
+
+// Invoked when received SET_REPORT control request or
+// received data on OUT endpoint ( Report ID = 0, Type = 0 )
+void tud_hid_set_report_cb(uint8_t report_id, hid_report_type_t report_type, uint8_t const* buffer, uint16_t bufsize);
 
-static inline bool tud_hid_mouse_button_press(uint8_t report_id, uint8_t buttons)
-{
-  return tud_hid_mouse_report(report_id, buttons, 0, 0, 0, 0);
-}
+// Invoked when received SET_PROTOCOL request ( mode switch Boot <-> Report )
+ATTR_WEAK void tud_hid_boot_mode_cb(uint8_t boot_mode);
 
-static inline bool tud_hid_mouse_button_release(uint8_t report_id)
-{
-  return tud_hid_mouse_report(report_id, 0, 0, 0, 0, 0);
-}
+// Invoked when received SET_IDLE request. return false will stall the request
+// - Idle Rate = 0 : only send report if there is changes, i.e skip duplication
+// - Idle Rate > 0 : skip duplication, but send at least 1 report every idle rate (in unit of 4 ms).
+ATTR_WEAK bool tud_hid_set_idle_cb(uint8_t idle_rate);
 
 /* --------------------------------------------------------------------+
  * HID Report Descriptor Template
@@ -107,23 +93,24 @@ static inline bool tud_hid_mouse_button_release(uint8_t report_id)
  * empty if multiple reports is not used
  *
  * - Only 1 report: no parameter
- *      uint8_t const report_desc[] = { HID_REPORT_DESC_KEYBOARD() };
+ *      uint8_t const report_desc[] = { TUD_HID_REPORT_DESC_KEYBOARD() };
  *
  * - Multiple Reports: "HID_REPORT_ID(ID)," must be passed to template
  *      uint8_t const report_desc[] =
  *      {
- *          HID_REPORT_DESC_KEYBOARD( HID_REPORT_ID(1), ) ,
- *          HID_REPORT_DESC_MOUSE   ( HID_REPORT_ID(2), )
+ *          TUD_HID_REPORT_DESC_KEYBOARD( HID_REPORT_ID(1), ) ,
+ *          TUD_HID_REPORT_DESC_MOUSE   ( HID_REPORT_ID(2), )
  *      };
  *--------------------------------------------------------------------*/
 
 // Keyboard Report Descriptor Template
-#define HID_REPORT_DESC_KEYBOARD(...) \
+#define TUD_HID_REPORT_DESC_KEYBOARD(...) \
   HID_USAGE_PAGE ( HID_USAGE_PAGE_DESKTOP     )                    ,\
   HID_USAGE      ( HID_USAGE_DESKTOP_KEYBOARD )                    ,\
   HID_COLLECTION ( HID_COLLECTION_APPLICATION )                    ,\
-    /* 8 bits Modifier Keys (Shfit, Control, Alt) */ \
+    /* Report ID if any */\
     __VA_ARGS__ \
+    /* 8 bits Modifier Keys (Shfit, Control, Alt) */ \
     HID_USAGE_PAGE ( HID_USAGE_PAGE_KEYBOARD )                     ,\
       HID_USAGE_MIN    ( 224                                    )  ,\
       HID_USAGE_MAX    ( 231                                    )  ,\
@@ -159,50 +146,60 @@ static inline bool tud_hid_mouse_button_release(uint8_t report_id)
   HID_COLLECTION_END \
 
 // Mouse Report Descriptor Template
-#define HID_REPORT_DESC_MOUSE(...) \
-  HID_USAGE_PAGE ( HID_USAGE_PAGE_DESKTOP      )                    ,\
-  HID_USAGE      ( HID_USAGE_DESKTOP_MOUSE     )                    ,\
-  HID_COLLECTION ( HID_COLLECTION_APPLICATION  )                    ,\
+#define TUD_HID_REPORT_DESC_MOUSE(...) \
+  HID_USAGE_PAGE ( HID_USAGE_PAGE_DESKTOP      )                   ,\
+  HID_USAGE      ( HID_USAGE_DESKTOP_MOUSE     )                   ,\
+  HID_COLLECTION ( HID_COLLECTION_APPLICATION  )                   ,\
+    /* Report ID if any */\
     __VA_ARGS__ \
-    HID_USAGE      ( HID_USAGE_DESKTOP_POINTER )                    ,\
-    HID_COLLECTION ( HID_COLLECTION_PHYSICAL   )                    ,\
-      HID_USAGE_PAGE  ( HID_USAGE_PAGE_BUTTON  )                    ,\
-        HID_USAGE_MIN    ( 1                                      ) ,\
-        HID_USAGE_MAX    ( 3                                      ) ,\
-        HID_LOGICAL_MIN  ( 0                                      ) ,\
-        HID_LOGICAL_MAX  ( 1                                      ) ,\
-        /* Left, Right, Middle, Backward, Forward mouse buttons */   \
-        HID_REPORT_COUNT ( 3                                      ) ,\
-        HID_REPORT_SIZE  ( 1                                      ) ,\
-        HID_INPUT        ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ) ,\
+    HID_USAGE      ( HID_USAGE_DESKTOP_POINTER )                   ,\
+    HID_COLLECTION ( HID_COLLECTION_PHYSICAL   )                   ,\
+      HID_USAGE_PAGE  ( HID_USAGE_PAGE_BUTTON  )                   ,\
+        HID_USAGE_MIN   ( 1                                      ) ,\
+        HID_USAGE_MAX   ( 5                                      ) ,\
+        HID_LOGICAL_MIN ( 0                                      ) ,\
+        HID_LOGICAL_MAX ( 1                                      ) ,\
+        /* Left, Right, Middle, Backward, Forward buttons */ \
+        HID_REPORT_COUNT( 5                                      ) ,\
+        HID_REPORT_SIZE ( 1                                      ) ,\
+        HID_INPUT       ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ) ,\
         /* 3 bit padding */ \
-        HID_REPORT_COUNT ( 1                                      ) ,\
-        HID_REPORT_SIZE  ( 5                                      ) ,\
-        HID_INPUT        ( HID_CONSTANT                           ) ,\
-      HID_USAGE_PAGE  ( HID_USAGE_PAGE_DESKTOP )                    ,\
+        HID_REPORT_COUNT( 1                                      ) ,\
+        HID_REPORT_SIZE ( 3                                      ) ,\
+        HID_INPUT       ( HID_CONSTANT                           ) ,\
+      HID_USAGE_PAGE  ( HID_USAGE_PAGE_DESKTOP )                   ,\
         /* X, Y position [-127, 127] */ \
-        HID_USAGE        ( HID_USAGE_DESKTOP_X                    ) ,\
-        HID_USAGE        ( HID_USAGE_DESKTOP_Y                    ) ,\
-        HID_LOGICAL_MIN  ( 0x81                                   ) ,\
-        HID_LOGICAL_MAX  ( 0x7f                                   ) ,\
-        HID_REPORT_COUNT ( 2                                      ) ,\
-        HID_REPORT_SIZE  ( 8                                      ) ,\
-        HID_INPUT        ( HID_DATA | HID_VARIABLE | HID_RELATIVE ) ,\
-        /* Mouse scroll [-127, 127] */ \
+        HID_USAGE       ( HID_USAGE_DESKTOP_X                    ) ,\
+        HID_USAGE       ( HID_USAGE_DESKTOP_Y                    ) ,\
+        HID_LOGICAL_MIN ( 0x81                                   ) ,\
+        HID_LOGICAL_MAX ( 0x7f                                   ) ,\
+        HID_REPORT_COUNT( 2                                      ) ,\
+        HID_REPORT_SIZE ( 8                                      ) ,\
+        HID_INPUT       ( HID_DATA | HID_VARIABLE | HID_RELATIVE ) ,\
+        /* Verital wheel scroll [-127, 127] */ \
         HID_USAGE       ( HID_USAGE_DESKTOP_WHEEL                )  ,\
         HID_LOGICAL_MIN ( 0x81                                   )  ,\
         HID_LOGICAL_MAX ( 0x7f                                   )  ,\
         HID_REPORT_COUNT( 1                                      )  ,\
         HID_REPORT_SIZE ( 8                                      )  ,\
         HID_INPUT       ( HID_DATA | HID_VARIABLE | HID_RELATIVE )  ,\
-    HID_COLLECTION_END                                              ,\
+      HID_USAGE_PAGE  ( HID_USAGE_PAGE_CONSUMER ), \
+       /* Horizontal wheel scroll [-127, 127] */ \
+        HID_USAGE_N     ( HID_USAGE_CONSUMER_AC_PAN, 2           ), \
+        HID_LOGICAL_MIN ( 0x81                                   ), \
+        HID_LOGICAL_MAX ( 0x7f                                   ), \
+        HID_REPORT_COUNT( 1                                      ), \
+        HID_REPORT_SIZE ( 8                                      ), \
+        HID_INPUT       ( HID_DATA | HID_VARIABLE | HID_RELATIVE ), \
+    HID_COLLECTION_END                                            , \
   HID_COLLECTION_END \
 
 // Consumer Control Report Descriptor Template
-#define HID_REPORT_DESC_CONSUMER(...) \
+#define TUD_HID_REPORT_DESC_CONSUMER(...) \
   HID_USAGE_PAGE ( HID_USAGE_PAGE_CONSUMER    )              ,\
   HID_USAGE      ( HID_USAGE_CONSUMER_CONTROL )              ,\
   HID_COLLECTION ( HID_COLLECTION_APPLICATION )              ,\
+    /* Report ID if any */\
     __VA_ARGS__ \
     HID_LOGICAL_MIN  ( 0x00                                ) ,\
     HID_LOGICAL_MAX_N( 0x03FF, 2                           ) ,\
@@ -219,10 +216,11 @@ static inline bool tud_hid_mouse_button_release(uint8_t report_id)
  * 0x02 - Standby
  * 0x04 - Wake Host
  */
-#define HID_REPORT_DESC_SYSTEM_CONTROL(...) \
+#define TUD_HID_REPORT_DESC_SYSTEM_CONTROL(...) \
   HID_USAGE_PAGE ( HID_USAGE_PAGE_DESKTOP           )        ,\
   HID_USAGE      ( HID_USAGE_DESKTOP_SYSTEM_CONTROL )        ,\
   HID_COLLECTION ( HID_COLLECTION_APPLICATION       )        ,\
+    /* Report ID if any */\
     __VA_ARGS__ \
     /* 2 bit system power control */ \
     HID_LOGICAL_MIN  ( 1                                   ) ,\
@@ -242,10 +240,11 @@ static inline bool tud_hid_mouse_button_release(uint8_t report_id)
 // Gamepad Report Descriptor Template
 // with 16 buttons and 2 joysticks with following layout
 // | Button Map (2 bytes) |  X | Y | Z | Rz
-#define HID_REPORT_DESC_GAMEPAD(...) \
+#define TUD_HID_REPORT_DESC_GAMEPAD(...) \
   HID_USAGE_PAGE ( HID_USAGE_PAGE_DESKTOP     )        ,\
   HID_USAGE      ( HID_USAGE_DESKTOP_GAMEPAD  )        ,\
   HID_COLLECTION ( HID_COLLECTION_APPLICATION )        ,\
+    /* Report ID if any */\
     __VA_ARGS__ \
     /* 16 bit Button Map */ \
     HID_USAGE_PAGE   ( HID_USAGE_PAGE_BUTTON                  ) ,\
@@ -269,6 +268,31 @@ static inline bool tud_hid_mouse_button_release(uint8_t report_id)
     HID_INPUT        ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ) ,\
   HID_COLLECTION_END \
 
+// HID Generic Input & Output
+// - 1st parameter is report size (mandatory)
+// - 2nd parameter is report id HID_REPORT_ID(n) (optional)
+#define TUD_HID_REPORT_DESC_GENERIC_INOUT(report_size, ...) \
+    HID_USAGE_PAGE_N ( HID_USAGE_PAGE_VENDOR, 2   ),\
+    HID_USAGE        ( 0x01                       ),\
+    HID_COLLECTION   ( HID_COLLECTION_APPLICATION ),\
+      /* Report ID if any */\
+      __VA_ARGS__ \
+      /* Input */ \
+      HID_USAGE       ( 0x02                                   ),\
+      HID_LOGICAL_MIN ( 0x00                                   ),\
+      HID_LOGICAL_MAX ( 0xff                                   ),\
+      HID_REPORT_SIZE ( 8                                      ),\
+      HID_REPORT_COUNT( report_size                            ),\
+      HID_INPUT       ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ),\
+      /* Output */ \
+      HID_USAGE       ( 0x03                                    ),\
+      HID_LOGICAL_MIN ( 0x00                                    ),\
+      HID_LOGICAL_MAX ( 0xff                                    ),\
+      HID_REPORT_SIZE ( 8                                       ),\
+      HID_REPORT_COUNT( report_size                             ),\
+      HID_OUTPUT      ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE  ),\
+    HID_COLLECTION_END \
+
 /*--------------------------------------------------------------------
  * ASCII to KEYCODE Conversion
  *  Expand to array of [128][2] (shift, keycode)

+ 11 - 13
src/class/msc/msc_device.c

@@ -42,13 +42,9 @@ enum
   MSC_STAGE_STATUS
 };
 
-typedef struct {
-  CFG_TUSB_MEM_ALIGN msc_cbw_t  cbw;
-
-//#if defined (__ICCARM__) && (CFG_TUSB_MCU == OPT_MCU_LPC11UXX || CFG_TUSB_MCU == OPT_MCU_LPC13XX)
-//  uint8_t padding1[64-sizeof(msc_cbw_t)]; // IAR cannot align struct's member
-//#endif
-
+typedef struct
+{
+  CFG_TUSB_MEM_ALIGN msc_cbw_t cbw;
   CFG_TUSB_MEM_ALIGN msc_csw_t csw;
 
   uint8_t  itf_num;
@@ -66,7 +62,7 @@ typedef struct {
   uint8_t add_sense_qualifier;
 }mscd_interface_t;
 
-CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN static mscd_interface_t _mscd_itf = { 0 };
+CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN static mscd_interface_t _mscd_itf;
 CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN static uint8_t _mscd_buf[CFG_TUD_MSC_BUFSIZE];
 
 //--------------------------------------------------------------------+
@@ -135,9 +131,8 @@ bool mscd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t
 
   mscd_interface_t * p_msc = &_mscd_itf;
 
-  // Open endpoint pair with usbd helper
-  tusb_desc_endpoint_t const *p_desc_ep = (tusb_desc_endpoint_t const *) tu_desc_next( itf_desc );
-  TU_ASSERT( usbd_open_edpt_pair(rhport, p_desc_ep, TUSB_XFER_BULK, &p_msc->ep_out, &p_msc->ep_in) );
+  // Open endpoint pair
+  TU_ASSERT( usbd_open_edpt_pair(rhport, tu_desc_next(itf_desc), 2, TUSB_XFER_BULK, &p_msc->ep_out, &p_msc->ep_in) );
 
   p_msc->itf_num = itf_desc->bInterfaceNumber;
   (*p_len) = sizeof(tusb_desc_interface_t) + 2*sizeof(tusb_desc_endpoint_t);
@@ -163,9 +158,12 @@ bool mscd_control_request(uint8_t rhport, tusb_control_request_t const * p_reque
 
     case MSC_REQ_GET_MAX_LUN:
     {
+      uint8_t maxlun = 1;
+      if (tud_msc_maxlun_cb) maxlun = tud_msc_maxlun_cb();
+      TU_VERIFY(maxlun);
+
       // MAX LUN is minus 1 by specs
-      uint8_t maxlun = 0;
-      if (tud_msc_maxlun_cb) maxlun = tud_msc_maxlun_cb() -1;
+      maxlun--;
 
       usbd_control_xfer(rhport, p_request, &maxlun, 1);
     }

+ 8 - 6
src/class/msc/msc_device.h

@@ -64,10 +64,11 @@ TU_VERIFY_STATIC(CFG_TUD_MSC_BUFSIZE < UINT16_MAX, "Size is not correct");
 bool tud_msc_set_sense(uint8_t lun, uint8_t sense_key, uint8_t add_sense_code, uint8_t add_sense_qualifier);
 
 //--------------------------------------------------------------------+
-// APPLICATION CALLBACK (WEAK is optional)
+// Application Callbacks (WEAK is optional)
 //--------------------------------------------------------------------+
+
 /**
- * Callback invoked when received \ref SCSI_CMD_READ_10 command
+ * Invoked when received \ref SCSI_CMD_READ_10 command
  * \param[in]   lun         Logical unit number
  * \param[in]   lba         Logical Block Address to be read
  * \param[in]   offset      Byte offset from LBA
@@ -86,7 +87,7 @@ bool tud_msc_set_sense(uint8_t lun, uint8_t sense_key, uint8_t add_sense_code, u
 int32_t tud_msc_read10_cb (uint8_t lun, uint32_t lba, uint32_t offset, void* buffer, uint32_t bufsize);
 
 /**
- * Callback invoked when received \ref SCSI_CMD_WRITE_10 command
+ * Invoked when received \ref SCSI_CMD_WRITE_10 command
  * \param[in]   lun         Logical unit number
  * \param[in]   lba         Logical Block Address to be write
  * \param[in]   offset      Byte offset from LBA
@@ -104,7 +105,8 @@ int32_t tud_msc_read10_cb (uint8_t lun, uint32_t lba, uint32_t offset, void* buf
  */
 int32_t tud_msc_write10_cb (uint8_t lun, uint32_t lba, uint32_t offset, uint8_t* buffer, uint32_t bufsize);
 
-// Invoked to determine the disk size
+// Invoked when received SCSI_CMD_READ_CAPACITY_10 and SCSI_CMD_READ_FORMAT_CAPACITY to determine the disk size
+// Application update block count and block size
 void tud_msc_capacity_cb(uint8_t lun, uint32_t* block_count, uint16_t* block_size);
 
 /**
@@ -128,13 +130,13 @@ int32_t tud_msc_scsi_cb (uint8_t lun, uint8_t const scsi_cmd[16], void* buffer,
 
 /*------------- Optional callbacks -------------*/
 
-// Invoked to determine max LUN
+// Invoked when received GET_MAX_LUN request
 ATTR_WEAK uint8_t tud_msc_maxlun_cb(void);
 
 // Invoked when Read10 command is complete
 ATTR_WEAK void tud_msc_read10_complete_cb(uint8_t lun);
 
-// Invoke when Write10 command is complete
+// Invoke when Write10 command is complete, can be used to flush flash caching
 ATTR_WEAK void tud_msc_write10_complete_cb(uint8_t lun);
 
 // Invoked when command in tud_msc_scsi_cb is complete

+ 2 - 2
src/common/tusb_types.h

@@ -169,7 +169,7 @@ enum {
 
 #define TUSB_DESC_CONFIG_POWER_MA(x)  ((x)/2)
 
-/// Device State
+/// Device State TODO remove
 typedef enum
 {
   TUSB_DEVICE_STATE_UNPLUG = 0  ,
@@ -184,7 +184,7 @@ typedef enum
   XFER_RESULT_STALLED,
 }xfer_result_t;
 
-enum
+enum // TODO remove
 {
   DESC_OFFSET_LEN  = 0,
   DESC_OFFSET_TYPE = 1

+ 10 - 10
src/device/usbd.c

@@ -702,25 +702,25 @@ void dcd_event_xfer_complete (uint8_t rhport, uint8_t ep_addr, uint32_t xferred_
 // Helper
 //--------------------------------------------------------------------+
 
-// Helper to parse an pair of endpoint descriptors (IN & OUT)
-bool usbd_open_edpt_pair(uint8_t rhport, tusb_desc_endpoint_t const* ep_desc, uint8_t xfer_type, uint8_t* ep_out, uint8_t* ep_in)
+// Parse consecutive endpoint descriptors (IN & OUT)
+bool usbd_open_edpt_pair(uint8_t rhport, uint8_t const* p_desc, uint8_t ep_count, uint8_t xfer_type, uint8_t* ep_out, uint8_t* ep_in)
 {
-  for(int i=0; i<2; i++)
+  for(int i=0; i<ep_count; i++)
   {
-    TU_ASSERT(TUSB_DESC_ENDPOINT == ep_desc->bDescriptorType &&
-              xfer_type          == ep_desc->bmAttributes.xfer );
+    tusb_desc_endpoint_t const * desc_ep = (tusb_desc_endpoint_t const *) p_desc;
 
-    TU_ASSERT(dcd_edpt_open(rhport, ep_desc));
+    TU_VERIFY(TUSB_DESC_ENDPOINT == desc_ep->bDescriptorType && xfer_type == desc_ep->bmAttributes.xfer);
+    TU_ASSERT(dcd_edpt_open(rhport, desc_ep));
 
-    if ( tu_edpt_dir(ep_desc->bEndpointAddress) == TUSB_DIR_IN )
+    if ( tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_IN )
     {
-      (*ep_in) = ep_desc->bEndpointAddress;
+      (*ep_in) = desc_ep->bEndpointAddress;
     }else
     {
-      (*ep_out) = ep_desc->bEndpointAddress;
+      (*ep_out) = desc_ep->bEndpointAddress;
     }
 
-    ep_desc = (tusb_desc_endpoint_t const *) tu_desc_next(ep_desc);
+    p_desc = tu_desc_next(p_desc);
   }
 
   return true;

+ 18 - 2
src/device/usbd.h

@@ -107,7 +107,7 @@ ATTR_WEAK void tud_resume_cb(void);
 #define TUD_CDC_DESC_LEN  (8+9+5+5+4+5+7+9+7+7)
 
 // CDC Descriptor Template
-// interface number, string index, EP notification address and size, EP data address (out,in) and size.
+// Interface number, string index, EP notification address and size, EP data address (out, in) and size.
 #define TUD_CDC_DESCRIPTOR(_itfnum, _stridx, _ep_notif, _ep_notif_size, _epout, _epin, _epsize) \
   /* Interface Associate */\
   8, TUSB_DESC_INTERFACE_ASSOCIATION, _itfnum, 2, TUSB_CLASS_CDC, CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL, CDC_COMM_PROTOCOL_ATCOMMAND, 0,\
@@ -149,15 +149,31 @@ ATTR_WEAK void tud_resume_cb(void);
 // Length of template descriptor: 25 bytes
 #define TUD_HID_DESC_LEN    (9 + 9 + 7)
 
+// HID Input only descriptor
 // Interface number, string index, protocol, report descriptor len, EP In address, size & polling interval
 #define TUD_HID_DESCRIPTOR(_itfnum, _stridx, _boot_protocol, _report_desc_len, _epin, _epsize, _ep_interval) \
   /* Interface */\
   9, TUSB_DESC_INTERFACE, _itfnum, 0, 1, TUSB_CLASS_HID, (_boot_protocol) ? HID_SUBCLASS_BOOT : 0, _boot_protocol, _stridx,\
   /* HID descriptor */\
   9, HID_DESC_TYPE_HID, U16_TO_U8S_LE(0x0111), 0, 1, HID_DESC_TYPE_REPORT, U16_TO_U8S_LE(_report_desc_len),\
-  /* Endpoint descriptor */\
+  /* Endpoint In */\
   7, TUSB_DESC_ENDPOINT, _epin, TUSB_XFER_INTERRUPT, U16_TO_U8S_LE(_epsize), _ep_interval
 
+// Length of template descriptor: 32 bytes
+#define TUD_HID_INOUT_DESC_LEN    (9 + 9 + 7 + 7)
+
+// HID Input & Output descriptor
+// Interface number, string index, protocol, report descriptor len, EP In & Out address, size & polling interval
+#define TUD_HID_INOUT_DESCRIPTOR(_itfnum, _stridx, _boot_protocol, _report_desc_len, _epin, _epout, _epsize, _ep_interval) \
+  /* Interface */\
+  9, TUSB_DESC_INTERFACE, _itfnum, 0, 2, TUSB_CLASS_HID, (_boot_protocol) ? HID_SUBCLASS_BOOT : 0, _boot_protocol, _stridx,\
+  /* HID descriptor */\
+  9, HID_DESC_TYPE_HID, U16_TO_U8S_LE(0x0111), 0, 1, HID_DESC_TYPE_REPORT, U16_TO_U8S_LE(_report_desc_len),\
+  /* Endpoint In */\
+  7, TUSB_DESC_ENDPOINT, _epin, TUSB_XFER_INTERRUPT, U16_TO_U8S_LE(_epsize), _ep_interval,\
+  /* Endpoint Out */\
+  7, TUSB_DESC_ENDPOINT, _epout, TUSB_XFER_INTERRUPT, U16_TO_U8S_LE(_epsize), _ep_interval
+
 #ifdef __cplusplus
  }
 #endif

+ 1 - 2
src/device/usbd_pvt.h

@@ -53,9 +53,8 @@ bool usbd_edpt_stalled(uint8_t rhport, uint8_t ep_addr);
 /*------------------------------------------------------------------*/
 /* Helper
  *------------------------------------------------------------------*/
-// helper to parse an pair of In and Out endpoint descriptors. They must be consecutive
-bool usbd_open_edpt_pair(uint8_t rhport, tusb_desc_endpoint_t const* p_desc_ep, uint8_t xfer_type, uint8_t* ep_out, uint8_t* ep_in);
 
+bool usbd_open_edpt_pair(uint8_t rhport, uint8_t const* p_desc, uint8_t ep_count, uint8_t xfer_type, uint8_t* ep_out, uint8_t* ep_in);
 void usbd_defer_func( osal_task_func_t func, void* param, bool in_isr );
 
 

+ 30 - 37
src/osal/osal.h

@@ -47,56 +47,49 @@ enum
 
 typedef void (*osal_task_func_t)( void * );
 
+#if CFG_TUSB_OS == OPT_OS_NONE
+  #include "osal_none.h"
+#elif CFG_TUSB_OS == OPT_OS_FREERTOS
+  #include "osal_freertos.h"
+#elif CFG_TUSB_OS == OPT_OS_MYNEWT
+  #include "osal_mynewt.h"
+#else
+  #error OS is not supported yet
+#endif
+
 //--------------------------------------------------------------------+
 // OSAL Porting API
 //--------------------------------------------------------------------+
-#if 0
-void osal_task_delay(uint32_t msec);
+static inline void osal_task_delay(uint32_t msec);
 
 //------------- Semaphore -------------//
-osal_semaphore_t osal_semaphore_create(osal_semaphore_def_t* semdef);
-bool osal_semaphore_post(osal_semaphore_t sem_hdl, bool in_isr);
-bool osal_semaphore_wait (osal_semaphore_t sem_hdl, uint32_t msec);
+static inline osal_semaphore_t osal_semaphore_create(osal_semaphore_def_t* semdef);
+static inline bool osal_semaphore_post(osal_semaphore_t sem_hdl, bool in_isr);
+static inline bool osal_semaphore_wait(osal_semaphore_t sem_hdl, uint32_t msec);
 
-void osal_semaphore_reset(osal_semaphore_t sem_hdl); // TODO removed
+static inline void osal_semaphore_reset(osal_semaphore_t sem_hdl); // TODO removed
 
 //------------- Mutex -------------//
-osal_mutex_t osal_mutex_create(osal_mutex_def_t* mdef);
-bool osal_mutex_lock (osal_mutex_t sem_hdl, uint32_t msec);
-bool osal_mutex_unlock(osal_mutex_t mutex_hdl);
+static inline osal_mutex_t osal_mutex_create(osal_mutex_def_t* mdef);
+static inline bool osal_mutex_lock (osal_mutex_t sem_hdl, uint32_t msec);
+static inline bool osal_mutex_unlock(osal_mutex_t mutex_hdl);
 
 //------------- Queue -------------//
-osal_queue_t osal_queue_create(osal_queue_def_t* qdef);
-bool osal_queue_receive(osal_queue_t const qhdl, void* data);
-bool osal_queue_send(osal_queue_t const qhdl, void const * data, bool in_isr);
-#endif
-
-#if CFG_TUSB_OS == OPT_OS_NONE
-  #include "osal_none.h"
-#else
-  #if CFG_TUSB_OS == OPT_OS_FREERTOS
-    #include "osal_freertos.h"
-  #elif CFG_TUSB_OS == OPT_OS_MYNEWT
-    #include "osal_mynewt.h"
-  #else
-    #error CFG_TUSB_OS is not defined or OS is not supported yet
-  #endif
-
-  // TODO remove subtask related macros later
-
-  //------------- Sub Task -------------//
-  #define OSAL_SUBTASK_BEGIN
-  #define OSAL_SUBTASK_END                    return TUSB_ERROR_NONE;
+static inline osal_queue_t osal_queue_create(osal_queue_def_t* qdef);
+static inline bool osal_queue_receive(osal_queue_t const qhdl, void* data);
+static inline bool osal_queue_send(osal_queue_t const qhdl, void const * data, bool in_isr);
 
-  #define STASK_RETURN(_error)                return _error;
-  #define STASK_INVOKE(_subtask, _status)     (_status) = _subtask
+#if 0  // TODO remove subtask related macros later
+// Sub Task
+#define OSAL_SUBTASK_BEGIN
+#define OSAL_SUBTASK_END                    return TUSB_ERROR_NONE;
 
-  //------------- Sub Task Assert -------------//
-  #define STASK_ASSERT_ERR(_err)              TU_VERIFY_ERR(_err)
-  #define STASK_ASSERT_ERR_HDLR(_err, _func)  TU_VERIFY_ERR_HDLR(_err, _func)
+#define STASK_RETURN(_error)                return _error;
+#define STASK_INVOKE(_subtask, _status)     (_status) = _subtask
 
-  #define STASK_ASSERT(_cond)                 TU_VERIFY(_cond, TUSB_ERROR_OSAL_TASK_FAILED)
-  #define STASK_ASSERT_HDLR(_cond, _func)     TU_VERIFY_HDLR(_cond, _func)
+// Sub Task Assert
+#define STASK_ASSERT_ERR(_err)              TU_VERIFY_ERR(_err)
+#define STASK_ASSERT(_cond)                 TU_VERIFY(_cond, TUSB_ERROR_OSAL_TASK_FAILED)
 #endif
 
 #ifdef __cplusplus

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

@@ -489,7 +489,7 @@ void USBD_IRQHandler(void)
       {
         xfer->total_len = xfer->actual_len;
 
-        // BULK/INT OUT complete
+        // CBI OUT complete
         dcd_event_xfer_complete(0, epnum, xfer->actual_len, XFER_RESULT_SUCCESS, true);
       }
     }
@@ -524,7 +524,7 @@ void USBD_IRQHandler(void)
           xact_in_prepare(epnum);
         } else
         {
-          // Bulk/Int IN complete
+          // CBI IN complete
           dcd_event_xfer_complete(0, epnum | TUSB_DIR_IN_MASK, xfer->actual_len, XFER_RESULT_SUCCESS, true);
         }
       }

+ 2 - 2
tests/support/descriptor_test.c

@@ -166,7 +166,7 @@ const app_configuration_desc_t desc_configuration =
         .bLength           = sizeof(tusb_hid_descriptor_hid_t),
         .bDescriptorType   = HID_DESC_TYPE_HID,
         .bcdHID            = 0x0111,
-        .bCountryCode      = HID_Local_NotSupported,
+        .bCountryCode      = HID_LOCAL_NotSupported,
         .bNumDescriptors   = 1,
         .bReportType       = HID_DESC_TYPE_REPORT,
         .wReportLength     = sizeof(keyboard_report_descriptor)
@@ -201,7 +201,7 @@ const app_configuration_desc_t desc_configuration =
         .bLength           = sizeof(tusb_hid_descriptor_hid_t),
         .bDescriptorType   = HID_DESC_TYPE_HID,
         .bcdHID            = 0x0111,
-        .bCountryCode      = HID_Local_NotSupported,
+        .bCountryCode      = HID_LOCAL_NotSupported,
         .bNumDescriptors   = 1,
         .bReportType       = HID_DESC_TYPE_REPORT,
         .wReportLength     = sizeof(mouse_report_descriptor)

+ 47 - 4
tools/build_all.py

@@ -4,10 +4,53 @@ import sys
 import subprocess
 import time
 
-all_device_example = ["cdc_msc_hid", "msc_dual_lun"]
-all_boards = ["metro_m0_express", "metro_m4_express", "pca10056", "stm32f407g_disc1"]
+travis = False
+if "TRAVIS" in os.environ and os.environ["TRAVIS"] == "true":
+    PARALLEL="-j 2"
+    travis = True
+
+success_count = 0
+fail_count = 0
+exit_status = 0
+
+all_device_example = ["cdc_msc_hid", "msc_dual_lun", "hid_generic_inout"]
+all_boards = ["metro_m0_express", "metro_m4_express", "pca10056", "feather_nrf52840_express", "stm32f407g_disc1"]
+
+def build_example(example, board):
+    subprocess.run("make -C examples/device/{} BOARD={} clean".format(example, board), shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+    return subprocess.run("make -j 4 -C examples/device/{} BOARD={} all".format(example, board), shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+
+total_time = time.monotonic()
 
 for example in all_device_example:
     for board in all_boards:
-        subprocess.run("make -j2 -C examples/device/{} BOARD={} clean".format(example, board), shell=True)
-        subprocess.run("make -j2 -C examples/device/{} BOARD={} all".format(example, board), shell=True)
+        start_time = time.monotonic()
+        build_result = build_example(example, board)
+        build_duration = time.monotonic() - start_time
+
+        if build_result.returncode != 0:
+            exit_status = build_result.returncode
+            success = "\033[31mfailed\033[0m"
+            fail_count += 1
+        else:
+            success = "\033[32msucceeded\033[0m"
+            success_count += 1
+
+        if travis:
+            print('travis_fold:start:build-{}-{}\\r'.format(example, board))
+        print("Build {} on {} took {:.2f}s and {}".format(example, board, build_duration, success))
+        if build_result.returncode != 0:
+            print(build_result.stdout.decode("utf-8"))
+        if travis:
+            print('travis_fold:end:build-{}-{}\\r'.format(example, board))
+
+# FreeRTOS example
+#example = 'cdc_msc_hid_freertos'
+#board = 'pca10056'
+#subprocess.run("make -j2 -C examples/device/{} BOARD={} clean all".format(example, board), shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+
+
+total_time = time.monotonic() - total_time
+
+print("Total build time took {:.2f}s".format(total_time))
+sys.exit(exit_status)