Parcourir la source

Merge pull request #59 from hathach/develop

add multiple Lun support along with msc_dual_lun example
hathach il y a 6 ans
Parent
commit
e4be6dd95e

+ 1 - 5
.travis.yml

@@ -17,8 +17,4 @@ before_script:
   - (! var_search "${TRAVIS_SDK-}" arm || arm-none-eabi-gcc --version)
 
 script:
-  - make -j2 -C examples/device/cdc_msc_hid BOARD=metro_m0_express
-  - make -j2 -C examples/device/cdc_msc_hid BOARD=metro_m4_express
-  - make -j2 -C examples/device/cdc_msc_hid BOARD=stm32f407g_disc1
-  - make -j2 -C examples/device/cdc_msc_hid BOARD=pca10056
-
+  - python3 tools/build_all.py

+ 1 - 1
README.md

@@ -25,7 +25,7 @@ Support multiple device configurations by dynamically changing usb descriptors.
 
 - Communication Class (CDC)
 - Human Interface Device (HID): Keyboard, Mouse, Gamepad etc ...
-- Mass Storage Class (MSC)
+- Mass Storage Class (MSC): with multiple LUNs
 - Musical Instrument Digital Interface (MIDI)
 
 ## Host Stack

+ 1 - 5
examples/device/cdc_msc_hid/Makefile

@@ -83,11 +83,7 @@ endif
 
 LIBS = -lgcc -lc -lm -lnosys
 
-EXAMPLE_SOURCE += \
-	src/main.c \
-	src/msc_disk.c \
-	src/usb_descriptors.c
-
+EXAMPLE_SOURCE += $(wildcard src/*.c)
 SRC_C += $(addprefix $(CURRENT_PATH)/, $(EXAMPLE_SOURCE))
 
 LIB_SOURCE += \

+ 45 - 14
examples/device/cdc_msc_hid/src/msc_disk.c

@@ -50,42 +50,72 @@ const
 #endif
 uint8_t msc_disk[DISK_BLOCK_NUM][DISK_BLOCK_SIZE] =
 {
-  //------------- Boot Sector -------------//
+  //------------- Block0: Boot Sector -------------//
   // byte_per_sector    = DISK_BLOCK_SIZE; fat12_sector_num_16  = DISK_BLOCK_NUM;
   // sector_per_cluster = 1; reserved_sectors = 1;
   // fat_num            = 1; fat12_root_entry_num = 16;
   // sector_per_fat     = 1; sector_per_track = 1; head_num = 1; hidden_sectors = 0;
   // drive_number       = 0x80; media_type = 0xf8; extended_boot_signature = 0x29;
-  // filesystem_type    = "FAT12   "; volume_serial_number = 0x1234; volume_label = "tinyusb msc";
-  [0] =
+  // filesystem_type    = "FAT12   "; volume_serial_number = 0x1234; volume_label = "TinyUSB MSC";
+  // FAT magic code at offset 510-511
   {
       0xEB, 0x3C, 0x90, 0x4D, 0x53, 0x44, 0x4F, 0x53, 0x35, 0x2E, 0x30, 0x00, 0x02, 0x01, 0x01, 0x00,
       0x01, 0x10, 0x00, 0x10, 0x00, 0xF8, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
-      0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x29, 0x34, 0x12, 0x00, 0x00, 0x74, 0x69, 0x6E, 0x79, 0x75,
-      0x73, 0x62, 0x20, 0x6D, 0x73, 0x63, 0x46, 0x41, 0x54, 0x31, 0x32, 0x20, 0x20, 0x20, 0x00, 0x00,
-      [510] = 0x55, [511] = 0xAA // FAT magic code
+      0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x29, 0x34, 0x12, 0x00, 0x00, 'T' , 'i' , 'n' , 'y' , 'U' ,
+      'S' , 'B' , ' ' , 'M' , 'S' , 'C' , 0x46, 0x41, 0x54, 0x31, 0x32, 0x20, 0x20, 0x20, 0x00, 0x00,
+      
+      // Zero up to 2 last bytes of FAT magic code
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+     
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0xAA
   },
 
-  //------------- FAT12 Table -------------//
-  [1] =
+  //------------- Block1: FAT12 Table -------------//
   {
       0xF8, 0xFF, 0xFF, 0xFF, 0x0F // // first 2 entries must be F8FF, third entry is cluster end of readme file
   },
 
-  //------------- Root Directory -------------//
-  [2] =
+  //------------- Block2: Root Directory -------------//
   {
       // first entry is volume label
-      0x54, 0x49, 0x4E, 0x59, 0x55, 0x53, 0x42, 0x20, 0x4D, 0x53, 0x43, 0x08, 0x00, 0x00, 0x00, 0x00,
+      'T' , 'i' , 'n' , 'y' , 'U' , 'S' , 'B' , ' ' , 'M' , 'S' , 'C' , 0x08, 0x00, 0x00, 0x00, 0x00,
       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4F, 0x6D, 0x65, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
       // second entry is readme file
       'R' , 'E' , 'A' , 'D' , 'M' , 'E' , ' ' , ' ' , 'T' , 'X' , 'T' , 0x20, 0x00, 0xC6, 0x52, 0x6D,
       0x65, 0x43, 0x65, 0x43, 0x00, 0x00, 0x88, 0x6D, 0x65, 0x43, 0x02, 0x00,
-      sizeof(README_CONTENTS)-1, 0x00, 0x00, 0x00 // readme's files ize (4 Bytes)
+      sizeof(README_CONTENTS)-1, 0x00, 0x00, 0x00 // readme's files size (4 Bytes)
   },
 
-  //------------- Readme Content -------------//
-  [3] = README_CONTENTS
+  //------------- Block3: Readme Content -------------//
+  README_CONTENTS
 };
 
 
@@ -117,6 +147,7 @@ int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset, uint8_t*
   return bufsize;
 }
 
+// Callback invoked to determine disk's size
 void tud_msc_capacity_cb(uint8_t lun, uint32_t* block_count, uint16_t* block_size)
 {
   (void) lun;

+ 1 - 10
examples/device/cdc_msc_hid/src/tusb_config.h

@@ -1,4 +1,4 @@
-/* 
+/*
  * The MIT License (MIT)
  *
  * Copyright (c) 2018, hathach (tinyusb.org)
@@ -89,9 +89,6 @@
 // MSC
 //--------------------------------------------------------------------
 
-// Number of supported Logical Unit Number (At least 1)
-#define CFG_TUD_MSC_MAXLUN          1
-
 // Buffer size of Device Mass storage
 #define CFG_TUD_MSC_BUFSIZE         512
 
@@ -108,12 +105,6 @@
 // HID
 //--------------------------------------------------------------------
 
-/* Use the HID_ASCII_TO_KEYCODE lookup
- * This will occupies 256 bytes of ROM. It will also enable the use of extra APIs
- * - tud_hid_keyboard_send_char()
- */
-#define CFG_TUD_HID_ASCII_TO_KEYCODE_LOOKUP 0
-
 #ifdef __cplusplus
  }
 #endif

+ 44 - 15
examples/device/cdc_msc_hid_freertos/src/msc_disk.c

@@ -50,45 +50,74 @@ const
 #endif
 uint8_t msc_disk[DISK_BLOCK_NUM][DISK_BLOCK_SIZE] =
 {
-  //------------- Boot Sector -------------//
+  //------------- Block0: Boot Sector -------------//
   // byte_per_sector    = DISK_BLOCK_SIZE; fat12_sector_num_16  = DISK_BLOCK_NUM;
   // sector_per_cluster = 1; reserved_sectors = 1;
   // fat_num            = 1; fat12_root_entry_num = 16;
   // sector_per_fat     = 1; sector_per_track = 1; head_num = 1; hidden_sectors = 0;
   // drive_number       = 0x80; media_type = 0xf8; extended_boot_signature = 0x29;
-  // filesystem_type    = "FAT12   "; volume_serial_number = 0x1234; volume_label = "tinyusb msc";
-  [0] =
+  // filesystem_type    = "FAT12   "; volume_serial_number = 0x1234; volume_label = "TinyUSB MSC";
+  // FAT magic code at offset 510-511
   {
       0xEB, 0x3C, 0x90, 0x4D, 0x53, 0x44, 0x4F, 0x53, 0x35, 0x2E, 0x30, 0x00, 0x02, 0x01, 0x01, 0x00,
       0x01, 0x10, 0x00, 0x10, 0x00, 0xF8, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
-      0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x29, 0x34, 0x12, 0x00, 0x00, 0x74, 0x69, 0x6E, 0x79, 0x75,
-      0x73, 0x62, 0x20, 0x6D, 0x73, 0x63, 0x46, 0x41, 0x54, 0x31, 0x32, 0x20, 0x20, 0x20, 0x00, 0x00,
-      [510] = 0x55, [511] = 0xAA // FAT magic code
+      0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x29, 0x34, 0x12, 0x00, 0x00, 'T' , 'i' , 'n' , 'y' , 'U' ,
+      'S' , 'B' , ' ' , 'M' , 'S' , 'C' , 0x46, 0x41, 0x54, 0x31, 0x32, 0x20, 0x20, 0x20, 0x00, 0x00,
+
+      // Zero up to 2 last bytes of FAT magic code
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0xAA
   },
 
-  //------------- FAT12 Table -------------//
-  [1] =
+  //------------- Block1: FAT12 Table -------------//
   {
       0xF8, 0xFF, 0xFF, 0xFF, 0x0F // // first 2 entries must be F8FF, third entry is cluster end of readme file
   },
 
-  //------------- Root Directory -------------//
-  [2] =
+  //------------- Block2: Root Directory -------------//
   {
       // first entry is volume label
-      0x54, 0x49, 0x4E, 0x59, 0x55, 0x53, 0x42, 0x20, 0x4D, 0x53, 0x43, 0x08, 0x00, 0x00, 0x00, 0x00,
+      'T' , 'i' , 'n' , 'y' , 'U' , 'S' , 'B' , ' ' , 'M' , 'S' , 'C' , 0x08, 0x00, 0x00, 0x00, 0x00,
       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4F, 0x6D, 0x65, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
       // second entry is readme file
       'R' , 'E' , 'A' , 'D' , 'M' , 'E' , ' ' , ' ' , 'T' , 'X' , 'T' , 0x20, 0x00, 0xC6, 0x52, 0x6D,
       0x65, 0x43, 0x65, 0x43, 0x00, 0x00, 0x88, 0x6D, 0x65, 0x43, 0x02, 0x00,
-      sizeof(README_CONTENTS)-1, 0x00, 0x00, 0x00 // readme's files ize (4 Bytes)
+      sizeof(README_CONTENTS)-1, 0x00, 0x00, 0x00 // readme's files size (4 Bytes)
   },
 
-  //------------- Readme Content -------------//
-  [3] = README_CONTENTS
+  //------------- Block3: Readme Content -------------//
+  README_CONTENTS
 };
 
-
 // Callback invoked when received READ10 command.
 // Copy disk's data to buffer (up to bufsize) and return number of copied bytes.
 int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset, void* buffer, uint32_t bufsize)

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

@@ -89,9 +89,6 @@
 // MSC
 //--------------------------------------------------------------------
 
-// Number of supported Logical Unit Number (At least 1)
-#define CFG_TUD_MSC_MAXLUN          1
-
 // Buffer size of Device Mass storage
 #define CFG_TUD_MSC_BUFSIZE         512
 
@@ -108,12 +105,6 @@
 // HID
 //--------------------------------------------------------------------
 
-/* Use the HID_ASCII_TO_KEYCODE lookup
- * This will occupies 256 bytes of ROM. It will also enable the use of extra APIs
- * - tud_hid_keyboard_send_char()
- */
-#define CFG_TUD_HID_ASCII_TO_KEYCODE_LOOKUP 0
-
 #ifdef __cplusplus
  }
 #endif

+ 186 - 0
examples/device/msc_dual_lun/Makefile

@@ -0,0 +1,186 @@
+include ../../../tools/top.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
+
+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 ''
+
+clean:
+	rm -rf build-$(BOARD)

+ 115 - 0
examples/device/msc_dual_lun/src/main.c

@@ -0,0 +1,115 @@
+/* 
+ * 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"
+
+//--------------------------------------------------------------------+
+// 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;
+}
+
+//--------------------------------------------------------------------+
+// 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
+}

+ 310 - 0
examples/device/msc_dual_lun/src/msc_disk_dual.c

@@ -0,0 +1,310 @@
+/* 
+ * 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 "bsp/board.h"
+#include "tusb.h"
+
+#if CFG_TUD_MSC
+
+// Some MCU doesn't have enough 8KB SRAM to store the whole disk
+// We will use Flash as read-only disk
+#if CFG_TUSB_MCU == OPT_MCU_LPC13XX
+#define DISK_READONLY
+#endif
+
+enum
+{
+  DISK_BLOCK_NUM  = 16, // 8KB is the smallest size that windows allow to mount
+  DISK_BLOCK_SIZE = 512
+};
+
+
+//--------------------------------------------------------------------+
+// LUN 0
+//--------------------------------------------------------------------+
+#define README0_CONTENTS \
+"LUN0: This is tinyusb's MassStorage Class demo.\r\n\r\n\
+If you find any bugs or get any questions, feel free to file an\r\n\
+issue at github.com/hathach/tinyusb"
+
+
+#ifdef DISK_READONLY
+const
+#endif
+uint8_t msc_disk0[DISK_BLOCK_NUM][DISK_BLOCK_SIZE] =
+{
+  //------------- Block0: Boot Sector -------------//
+  // byte_per_sector    = DISK_BLOCK_SIZE; fat12_sector_num_16  = DISK_BLOCK_NUM;
+  // sector_per_cluster = 1; reserved_sectors = 1;
+  // fat_num            = 1; fat12_root_entry_num = 16;
+  // sector_per_fat     = 1; sector_per_track = 1; head_num = 1; hidden_sectors = 0;
+  // drive_number       = 0x80; media_type = 0xf8; extended_boot_signature = 0x29;
+  // filesystem_type    = "FAT12   "; volume_serial_number = 0x1234; volume_label = "TinyUSB 0  ";
+  // FAT magic code at offset 510-511
+  {
+      0xEB, 0x3C, 0x90, 0x4D, 0x53, 0x44, 0x4F, 0x53, 0x35, 0x2E, 0x30, 0x00, 0x02, 0x01, 0x01, 0x00,
+      0x01, 0x10, 0x00, 0x10, 0x00, 0xF8, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x29, 0x34, 0x12, 0x00, 0x00, 'T' , 'i' , 'n' , 'y' , 'U' ,
+      'S' , 'B' , ' ' , '0' , ' ' , ' ' , 0x46, 0x41, 0x54, 0x31, 0x32, 0x20, 0x20, 0x20, 0x00, 0x00,
+
+      // Zero up to 2 last bytes of FAT magic code
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0xAA
+  },
+
+  //------------- Block1: FAT12 Table -------------//
+  {
+      0xF8, 0xFF, 0xFF, 0xFF, 0x0F // // first 2 entries must be F8FF, third entry is cluster end of readme file
+  },
+
+  //------------- Block2: Root Directory -------------//
+  {
+      // first entry is volume label
+      'T' , 'i' , 'n' , 'y' , 'U' , 'S' , 'B' , ' ' , '0' , ' ' , ' ' , 0x08, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4F, 0x6D, 0x65, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      // second entry is readme file
+      'R' , 'E' , 'A' , 'D' , 'M' , 'E' , '0' , ' ' , 'T' , 'X' , 'T' , 0x20, 0x00, 0xC6, 0x52, 0x6D,
+      0x65, 0x43, 0x65, 0x43, 0x00, 0x00, 0x88, 0x6D, 0x65, 0x43, 0x02, 0x00,
+      sizeof(README0_CONTENTS)-1, 0x00, 0x00, 0x00 // readme's files size (4 Bytes)
+  },
+
+  //------------- Block3: Readme Content -------------//
+  README0_CONTENTS
+};
+
+//--------------------------------------------------------------------+
+// LUN 1
+//--------------------------------------------------------------------+
+#define README1_CONTENTS \
+"LUN1: This is tinyusb's MassStorage Class demo.\r\n\r\n\
+If you find any bugs or get any questions, feel free to file an\r\n\
+issue at github.com/hathach/tinyusb"
+
+#ifdef DISK_READONLY
+const
+#endif
+uint8_t msc_disk1[DISK_BLOCK_NUM][DISK_BLOCK_SIZE] =
+{
+  //------------- Block0: Boot Sector -------------//
+  // byte_per_sector    = DISK_BLOCK_SIZE; fat12_sector_num_16  = DISK_BLOCK_NUM;
+  // sector_per_cluster = 1; reserved_sectors = 1;
+  // fat_num            = 1; fat12_root_entry_num = 16;
+  // sector_per_fat     = 1; sector_per_track = 1; head_num = 1; hidden_sectors = 0;
+  // drive_number       = 0x80; media_type = 0xf8; extended_boot_signature = 0x29;
+  // filesystem_type    = "FAT12   "; volume_serial_number = 0x5678; volume_label = "TinyUSB 1  ";
+  // FAT magic code at offset 510-511
+  {
+      0xEB, 0x3C, 0x90, 0x4D, 0x53, 0x44, 0x4F, 0x53, 0x35, 0x2E, 0x30, 0x00, 0x02, 0x01, 0x01, 0x00,
+      0x01, 0x10, 0x00, 0x10, 0x00, 0xF8, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x29, 0x78, 0x56, 0x00, 0x00, 'T' , 'i' , 'n' , 'y' , 'U' ,
+      'S' , 'B' , ' ' , '1' , ' ' , ' ' , 0x46, 0x41, 0x54, 0x31, 0x32, 0x20, 0x20, 0x20, 0x00, 0x00,
+
+      // Zero up to 2 last bytes of FAT magic code
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0xAA
+  },
+
+  //------------- Block1: FAT12 Table -------------//
+  {
+      0xF8, 0xFF, 0xFF, 0xFF, 0x0F // // first 2 entries must be F8FF, third entry is cluster end of readme file
+  },
+
+  //------------- Block2: Root Directory -------------//
+  {
+      // first entry is volume label
+      'T' , 'i' , 'n' , 'y' , 'U' , 'S' , 'B' , ' ' , '1' , ' ' , ' ' , 0x08, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4F, 0x6D, 0x65, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      // second entry is readme file
+      'R' , 'E' , 'A' , 'D' , 'M' , 'E' , '1' , ' ' , 'T' , 'X' , 'T' , 0x20, 0x00, 0xC6, 0x52, 0x6D,
+      0x65, 0x43, 0x65, 0x43, 0x00, 0x00, 0x88, 0x6D, 0x65, 0x43, 0x02, 0x00,
+      sizeof(README1_CONTENTS)-1, 0x00, 0x00, 0x00 // readme's files size (4 Bytes)
+  },
+
+  //------------- Block3: Readme Content -------------//
+  README1_CONTENTS
+};
+
+// Invoked to determine max LUN
+uint8_t tud_msc_maxlun_cb(void)
+{
+  return 2; // dual LUN
+}
+
+// Callback invoked to determine disk's size
+void tud_msc_capacity_cb(uint8_t lun, uint32_t* block_count, uint16_t* block_size)
+{
+  (void) lun; // both LUNs have same size
+
+  *block_count = DISK_BLOCK_NUM;
+  *block_size  = DISK_BLOCK_SIZE;
+}
+
+
+// Callback invoked when received READ10 command.
+// Copy disk's data to buffer (up to bufsize) and return number of copied bytes.
+int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset, void* buffer, uint32_t bufsize)
+{
+  uint8_t const* addr = (lun ? msc_disk1[lba] : msc_disk0[lba]) + offset;
+  memcpy(buffer, addr, bufsize);
+
+  return bufsize;
+}
+
+// Callback invoked when received WRITE10 command.
+// Process data in buffer to disk's storage and return number of written bytes
+int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset, uint8_t* buffer, uint32_t bufsize)
+{
+#ifndef DISK_READONLY
+  uint8_t* addr = (lun ? msc_disk1[lba] : msc_disk0[lba])  + offset;
+  memcpy(addr, buffer, bufsize);
+#else
+  (void) lun; (void) lba; (void) offset; (void) buffer;
+#endif
+
+  return bufsize;
+}
+
+// Callback invoked when received an SCSI command not in built-in list below
+// - READ_CAPACITY10, READ_FORMAT_CAPACITY, INQUIRY, MODE_SENSE6, REQUEST_SENSE
+// - READ10 and WRITE10 has their own callbacks
+int32_t tud_msc_scsi_cb (uint8_t lun, uint8_t const scsi_cmd[16], void* buffer, uint16_t bufsize)
+{
+  // read10 & write10 has their own callback and MUST not be handled here
+
+  void const* response = NULL;
+  uint16_t resplen = 0;
+
+  // most scsi handled is input
+  bool in_xfer = true;
+
+  switch (scsi_cmd[0])
+  {
+    case SCSI_CMD_TEST_UNIT_READY:
+      // Command that host uses to check our readiness before sending other commands
+      resplen = 0;
+    break;
+
+    case SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL:
+      // Host is about to read/write etc ... better not to disconnect disk
+      resplen = 0;
+    break;
+
+    case SCSI_CMD_START_STOP_UNIT:
+      // Host try to eject/safe remove/poweroff us. We could safely disconnect with disk storage, or go into lower power
+      /* scsi_start_stop_unit_t const * start_stop = (scsi_start_stop_unit_t const *) scsi_cmd;
+        // Start bit = 0 : low power mode, if load_eject = 1 : unmount disk storage as well
+        // Start bit = 1 : Ready mode, if load_eject = 1 : mount disk storage
+        start_stop->start;
+        start_stop->load_eject;
+       */
+       resplen = 0;
+    break;
+
+
+    default:
+      // Set Sense = Invalid Command Operation
+      tud_msc_set_sense(lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00);
+
+      // negative means error -> tinyusb could stall and/or response with failed status
+      resplen = -1;
+    break;
+  }
+
+  // return resplen must not larger than bufsize
+  if ( resplen > bufsize ) resplen = bufsize;
+
+  if ( response && (resplen > 0) )
+  {
+    if(in_xfer)
+    {
+      memcpy(buffer, response, resplen);
+    }else
+    {
+      // SCSI output
+    }
+  }
+
+  return resplen;
+}
+
+#endif

+ 99 - 0
examples/device/msc_dual_lun/src/tusb_config.h

@@ -0,0 +1,99 @@
+/*
+ * 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                 1
+#define CFG_TUD_HID                 0
+#define CFG_TUD_MIDI                0
+#define CFG_TUD_CUSTOM_CLASS        0
+
+//--------------------------------------------------------------------
+// MSC
+//--------------------------------------------------------------------
+
+// Buffer size of Device Mass storage
+#define CFG_TUD_MSC_BUFSIZE         512
+
+// Vendor name included in Inquiry response, max 8 bytes
+#define CFG_TUD_MSC_VENDOR          "tinyusb"
+
+// Product name included in Inquiry response, max 16 bytes
+#define CFG_TUD_MSC_PRODUCT         "tusb msc"
+
+// Product revision string included in Inquiry response, max 4 bytes
+#define CFG_TUD_MSC_PRODUCT_REV     "1.0"
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif /* _TUSB_CONFIG_H_ */

+ 112 - 0
examples/device/msc_dual_lun/src/usb_descriptors.c

@@ -0,0 +1,112 @@
+/* 
+ * 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
+};
+
+//------------- Configuration Descriptor -------------//
+enum
+{
+  ITF_NUM_MSC,
+  ITF_NUM_TOTAL
+};
+
+enum
+{
+  CONFIG_TOTAL_LEN = TUD_CONFIG_DESC_LEN + TUD_MSC_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_MSC   0x02
+
+uint8_t const desc_configuration[] =
+{
+  // Config: self-powered with remote wakeup support, max power up to 100 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
+};
+
+//------------- 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   = NULL,
+};

+ 0 - 10
examples/host/cdc_msc_hid/src/tusb_config.h

@@ -97,8 +97,6 @@
 //--------------------------------------------------------------------
 // MSC
 //--------------------------------------------------------------------
-// Number of supported Logical Unit Number (At least 1)
-#define CFG_TUD_MSC_MAXLUN          1
 
 // Buffer size of Device Mass storage
 #define CFG_TUD_MSC_BUFSIZE         512
@@ -116,14 +114,6 @@
 // HID
 //--------------------------------------------------------------------
 
-/* Use the HID_ASCII_TO_KEYCODE lookup if CFG_TUD_HID_KEYBOARD is enabled.
- * This will occupies 256 bytes of ROM. It will also enable the use of 2 extra APIs
- * - tud_hid_keyboard_send_char()
- * - tud_hid_keyboard_send_string()
- */
-#define CFG_TUD_HID_ASCII_TO_KEYCODE_LOOKUP 1
-
-
 #ifdef __cplusplus
  }
 #endif

+ 0 - 152
src/class/hid/hid_device.c

@@ -129,19 +129,6 @@ bool tud_hid_keyboard_report(uint8_t report_id, uint8_t modifier, uint8_t keycod
   return tud_hid_report(report_id, &report, sizeof(report));
 }
 
-#if CFG_TUD_HID_ASCII_TO_KEYCODE_LOOKUP
-bool tud_hid_keyboard_key_press(uint8_t report_id, char ch)
-{
-  uint8_t keycode[6] = { 0 };
-  uint8_t modifier   = 0;
-
-  if ( HID_ASCII_TO_KEYCODE[(uint8_t)ch].shift ) modifier = KEYBOARD_MODIFIER_LEFTSHIFT;
-  keycode[0] = HID_ASCII_TO_KEYCODE[(uint8_t)ch].keycode;
-
-  return tud_hid_keyboard_report(report_id, modifier, keycode);
-}
-#endif // CFG_TUD_HID_ASCII_TO_KEYCODE_LOOKUP
-
 //--------------------------------------------------------------------+
 // MOUSE APPLICATION API
 //--------------------------------------------------------------------+
@@ -337,143 +324,4 @@ bool hidd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t
   return true;
 }
 
-
-//------------- Ascii to Keycode Lookup -------------//
-#if CFG_TUD_HID_ASCII_TO_KEYCODE_LOOKUP
-const hid_ascii_to_keycode_entry_t HID_ASCII_TO_KEYCODE[128] =
-{
-    {0, 0                     }, // 0x00 Null
-    {0, 0                     }, // 0x01
-    {0, 0                     }, // 0x02
-    {0, 0                     }, // 0x03
-    {0, 0                     }, // 0x04
-    {0, 0                     }, // 0x05
-    {0, 0                     }, // 0x06
-    {0, 0                     }, // 0x07
-    {0, HID_KEY_BACKSPACE     }, // 0x08 Backspace
-    {0, HID_KEY_TAB           }, // 0x09 Horizontal Tab
-    {0, HID_KEY_RETURN        }, // 0x0A Line Feed
-    {0, 0                     }, // 0x0B
-    {0, 0                     }, // 0x0C
-    {0, HID_KEY_RETURN        }, // 0x0D Carriage return
-    {0, 0                     }, // 0x0E
-    {0, 0                     }, // 0x0F
-    {0, 0                     }, // 0x10
-    {0, 0                     }, // 0x11
-    {0, 0                     }, // 0x12
-    {0, 0                     }, // 0x13
-    {0, 0                     }, // 0x14
-    {0, 0                     }, // 0x15
-    {0, 0                     }, // 0x16
-    {0, 0                     }, // 0x17
-    {0, 0                     }, // 0x18
-    {0, 0                     }, // 0x19
-    {0, 0                     }, // 0x1A
-    {0, HID_KEY_ESCAPE        }, // 0x1B Escape
-    {0, 0                     }, // 0x1C
-    {0, 0                     }, // 0x1D
-    {0, 0                     }, // 0x1E
-    {0, 0                     }, // 0x1F
-
-    {0, HID_KEY_SPACE         }, // 0x20
-    {1, HID_KEY_1             }, // 0x21 !
-    {1, HID_KEY_APOSTROPHE    }, // 0x22 "
-    {1, HID_KEY_3             }, // 0x23 #
-    {1, HID_KEY_4             }, // 0x24 $
-    {1, HID_KEY_5             }, // 0x25 %
-    {1, HID_KEY_7             }, // 0x26 &
-    {0, HID_KEY_APOSTROPHE    }, // 0x27 '
-    {1, HID_KEY_9             }, // 0x28 (
-    {1, HID_KEY_0             }, // 0x29 )
-    {1, HID_KEY_8             }, // 0x2A *
-    {1, HID_KEY_EQUAL         }, // 0x2B +
-    {0, HID_KEY_COMMA         }, // 0x2C ,
-    {0, HID_KEY_MINUS         }, // 0x2D -
-    {0, HID_KEY_PERIOD        }, // 0x2E .
-    {0, HID_KEY_SLASH         }, // 0x2F /
-    {0, HID_KEY_0             }, // 0x30 0
-    {0, HID_KEY_1             }, // 0x31 1
-    {0, HID_KEY_2             }, // 0x32 2
-    {0, HID_KEY_3             }, // 0x33 3
-    {0, HID_KEY_4             }, // 0x34 4
-    {0, HID_KEY_5             }, // 0x35 5
-    {0, HID_KEY_6             }, // 0x36 6
-    {0, HID_KEY_7             }, // 0x37 7
-    {0, HID_KEY_8             }, // 0x38 8
-    {0, HID_KEY_9             }, // 0x39 9
-    {1, HID_KEY_SEMICOLON     }, // 0x3A :
-    {0, HID_KEY_SEMICOLON     }, // 0x3B ;
-    {1, HID_KEY_COMMA         }, // 0x3C <
-    {0, HID_KEY_EQUAL         }, // 0x3D =
-    {1, HID_KEY_PERIOD        }, // 0x3E >
-    {1, HID_KEY_SLASH         }, // 0x3F ?
-
-    {1, HID_KEY_2             }, // 0x40 @
-    {1, HID_KEY_A             }, // 0x41 A
-    {1, HID_KEY_B             }, // 0x42 B
-    {1, HID_KEY_C             }, // 0x43 C
-    {1, HID_KEY_D             }, // 0x44 D
-    {1, HID_KEY_E             }, // 0x45 E
-    {1, HID_KEY_F             }, // 0x46 F
-    {1, HID_KEY_G             }, // 0x47 G
-    {1, HID_KEY_H             }, // 0x48 H
-    {1, HID_KEY_I             }, // 0x49 I
-    {1, HID_KEY_J             }, // 0x4A J
-    {1, HID_KEY_K             }, // 0x4B K
-    {1, HID_KEY_L             }, // 0x4C L
-    {1, HID_KEY_M             }, // 0x4D M
-    {1, HID_KEY_N             }, // 0x4E N
-    {1, HID_KEY_O             }, // 0x4F O
-    {1, HID_KEY_P             }, // 0x50 P
-    {1, HID_KEY_Q             }, // 0x51 Q
-    {1, HID_KEY_R             }, // 0x52 R
-    {1, HID_KEY_S             }, // 0x53 S
-    {1, HID_KEY_T             }, // 0x55 T
-    {1, HID_KEY_U             }, // 0x55 U
-    {1, HID_KEY_V             }, // 0x56 V
-    {1, HID_KEY_W             }, // 0x57 W
-    {1, HID_KEY_X             }, // 0x58 X
-    {1, HID_KEY_Y             }, // 0x59 Y
-    {1, HID_KEY_Z             }, // 0x5A Z
-    {0, HID_KEY_BRACKET_LEFT  }, // 0x5B [
-    {0, HID_KEY_BACKSLASH     }, // 0x5C '\'
-    {0, HID_KEY_BRACKET_RIGHT }, // 0x5D ]
-    {1, HID_KEY_6             }, // 0x5E ^
-    {1, HID_KEY_MINUS         }, // 0x5F _
-
-    {0, HID_KEY_GRAVE         }, // 0x60 `
-    {0, HID_KEY_A             }, // 0x61 a
-    {0, HID_KEY_B             }, // 0x62 b
-    {0, HID_KEY_C             }, // 0x63 c
-    {0, HID_KEY_D             }, // 0x66 d
-    {0, HID_KEY_E             }, // 0x65 e
-    {0, HID_KEY_F             }, // 0x66 f
-    {0, HID_KEY_G             }, // 0x67 g
-    {0, HID_KEY_H             }, // 0x68 h
-    {0, HID_KEY_I             }, // 0x69 i
-    {0, HID_KEY_J             }, // 0x6A j
-    {0, HID_KEY_K             }, // 0x6B k
-    {0, HID_KEY_L             }, // 0x6C l
-    {0, HID_KEY_M             }, // 0x6D m
-    {0, HID_KEY_N             }, // 0x6E n
-    {0, HID_KEY_O             }, // 0x6F o
-    {0, HID_KEY_P             }, // 0x70 p
-    {0, HID_KEY_Q             }, // 0x71 q
-    {0, HID_KEY_R             }, // 0x72 r
-    {0, HID_KEY_S             }, // 0x73 s
-    {0, HID_KEY_T             }, // 0x75 t
-    {0, HID_KEY_U             }, // 0x75 u
-    {0, HID_KEY_V             }, // 0x76 v
-    {0, HID_KEY_W             }, // 0x77 w
-    {0, HID_KEY_X             }, // 0x78 x
-    {0, HID_KEY_Y             }, // 0x79 y
-    {0, HID_KEY_Z             }, // 0x7A z
-    {1, HID_KEY_BRACKET_LEFT  }, // 0x7B {
-    {1, HID_KEY_BACKSLASH     }, // 0x7C |
-    {1, HID_KEY_BRACKET_RIGHT }, // 0x7D }
-    {1, HID_KEY_GRAVE         }, // 0x7E ~
-    {0, HID_KEY_DELETE        }  // 0x7F Delete
-};
-#endif // CFG_TUD_HID_ASCII_TO_KEYCODE_LOOKUP
-
 #endif

+ 149 - 15
src/class/hid/hid_device.h

@@ -38,9 +38,7 @@
 //--------------------------------------------------------------------+
 // Class Driver Default Configure & Validation
 //--------------------------------------------------------------------+
-#ifndef CFG_TUD_HID_ASCII_TO_KEYCODE_LOOKUP
-#define CFG_TUD_HID_ASCII_TO_KEYCODE_LOOKUP 0
-#endif
+
 
 //--------------------------------------------------------------------+
 // Application API
@@ -81,16 +79,6 @@ static inline bool tud_hid_keyboard_key_release(uint8_t report_id)
   return tud_hid_keyboard_report(report_id, 0, NULL);
 }
 
-#if CFG_TUD_HID_ASCII_TO_KEYCODE_LOOKUP
-bool tud_hid_keyboard_key_press(uint8_t report_id, char ch);
-
-typedef struct{
-  uint8_t shift;
-  uint8_t keycode;
-}hid_ascii_to_keycode_entry_t;
-extern const hid_ascii_to_keycode_entry_t HID_ASCII_TO_KEYCODE[128];
-#endif
-
 //--------------------------------------------------------------------+
 // MOUSE API
 // Convenient helper to send mouse report if application use standard/boot
@@ -281,8 +269,154 @@ static inline bool tud_hid_mouse_button_release(uint8_t report_id)
     HID_INPUT        ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ) ,\
   HID_COLLECTION_END \
 
-/** @} */
-/** @} */
+/*--------------------------------------------------------------------
+ * ASCII to KEYCODE Conversion
+ *  Expand to array of [128][2] (shift, keycode)
+ *
+ * Usage: example to convert input char into keyboard report (modifier + keycode)
+ *
+ *  uint8_t const conv_table[128][2] =  { HID_ASCII_TO_KEYCODE };
+ *
+ *  uint8_t keycode[6] = { 0 };
+ *  uint8_t modifier   = 0;
+ *
+ *  if ( conv_table[chr][0] ) modifier = KEYBOARD_MODIFIER_LEFTSHIFT;
+ *  keycode[0] = conv_table[chr][1];
+ *  tud_hid_keyboard_report(report_id, modifier, keycode);
+ *
+ *--------------------------------------------------------------------*/
+#define HID_ASCII_TO_KEYCODE \
+    {0, 0                     }, /* 0x00 Null      */ \
+    {0, 0                     }, /* 0x01           */ \
+    {0, 0                     }, /* 0x02           */ \
+    {0, 0                     }, /* 0x03           */ \
+    {0, 0                     }, /* 0x04           */ \
+    {0, 0                     }, /* 0x05           */ \
+    {0, 0                     }, /* 0x06           */ \
+    {0, 0                     }, /* 0x07           */ \
+    {0, HID_KEY_BACKSPACE     }, /* 0x08 Backspace */ \
+    {0, HID_KEY_TAB           }, /* 0x09 Tab       */ \
+    {0, HID_KEY_RETURN        }, /* 0x0A Line Feed */ \
+    {0, 0                     }, /* 0x0B           */ \
+    {0, 0                     }, /* 0x0C           */ \
+    {0, HID_KEY_RETURN        }, /* 0x0D CR        */ \
+    {0, 0                     }, /* 0x0E           */ \
+    {0, 0                     }, /* 0x0F           */ \
+    {0, 0                     }, /* 0x10           */ \
+    {0, 0                     }, /* 0x11           */ \
+    {0, 0                     }, /* 0x12           */ \
+    {0, 0                     }, /* 0x13           */ \
+    {0, 0                     }, /* 0x14           */ \
+    {0, 0                     }, /* 0x15           */ \
+    {0, 0                     }, /* 0x16           */ \
+    {0, 0                     }, /* 0x17           */ \
+    {0, 0                     }, /* 0x18           */ \
+    {0, 0                     }, /* 0x19           */ \
+    {0, 0                     }, /* 0x1A           */ \
+    {0, HID_KEY_ESCAPE        }, /* 0x1B Escape    */ \
+    {0, 0                     }, /* 0x1C           */ \
+    {0, 0                     }, /* 0x1D           */ \
+    {0, 0                     }, /* 0x1E           */ \
+    {0, 0                     }, /* 0x1F           */ \
+                                                      \
+    {0, HID_KEY_SPACE         }, /* 0x20           */ \
+    {1, HID_KEY_1             }, /* 0x21 !         */ \
+    {1, HID_KEY_APOSTROPHE    }, /* 0x22 "         */ \
+    {1, HID_KEY_3             }, /* 0x23 #         */ \
+    {1, HID_KEY_4             }, /* 0x24 $         */ \
+    {1, HID_KEY_5             }, /* 0x25 %         */ \
+    {1, HID_KEY_7             }, /* 0x26 &         */ \
+    {0, HID_KEY_APOSTROPHE    }, /* 0x27 '         */ \
+    {1, HID_KEY_9             }, /* 0x28 (         */ \
+    {1, HID_KEY_0             }, /* 0x29 )         */ \
+    {1, HID_KEY_8             }, /* 0x2A *         */ \
+    {1, HID_KEY_EQUAL         }, /* 0x2B +         */ \
+    {0, HID_KEY_COMMA         }, /* 0x2C ,         */ \
+    {0, HID_KEY_MINUS         }, /* 0x2D -         */ \
+    {0, HID_KEY_PERIOD        }, /* 0x2E .         */ \
+    {0, HID_KEY_SLASH         }, /* 0x2F /         */ \
+    {0, HID_KEY_0             }, /* 0x30 0         */ \
+    {0, HID_KEY_1             }, /* 0x31 1         */ \
+    {0, HID_KEY_2             }, /* 0x32 2         */ \
+    {0, HID_KEY_3             }, /* 0x33 3         */ \
+    {0, HID_KEY_4             }, /* 0x34 4         */ \
+    {0, HID_KEY_5             }, /* 0x35 5         */ \
+    {0, HID_KEY_6             }, /* 0x36 6         */ \
+    {0, HID_KEY_7             }, /* 0x37 7         */ \
+    {0, HID_KEY_8             }, /* 0x38 8         */ \
+    {0, HID_KEY_9             }, /* 0x39 9         */ \
+    {1, HID_KEY_SEMICOLON     }, /* 0x3A :         */ \
+    {0, HID_KEY_SEMICOLON     }, /* 0x3B ;         */ \
+    {1, HID_KEY_COMMA         }, /* 0x3C <         */ \
+    {0, HID_KEY_EQUAL         }, /* 0x3D =         */ \
+    {1, HID_KEY_PERIOD        }, /* 0x3E >         */ \
+    {1, HID_KEY_SLASH         }, /* 0x3F ?         */ \
+                                                      \
+    {1, HID_KEY_2             }, /* 0x40 @         */ \
+    {1, HID_KEY_A             }, /* 0x41 A         */ \
+    {1, HID_KEY_B             }, /* 0x42 B         */ \
+    {1, HID_KEY_C             }, /* 0x43 C         */ \
+    {1, HID_KEY_D             }, /* 0x44 D         */ \
+    {1, HID_KEY_E             }, /* 0x45 E         */ \
+    {1, HID_KEY_F             }, /* 0x46 F         */ \
+    {1, HID_KEY_G             }, /* 0x47 G         */ \
+    {1, HID_KEY_H             }, /* 0x48 H         */ \
+    {1, HID_KEY_I             }, /* 0x49 I         */ \
+    {1, HID_KEY_J             }, /* 0x4A J         */ \
+    {1, HID_KEY_K             }, /* 0x4B K         */ \
+    {1, HID_KEY_L             }, /* 0x4C L         */ \
+    {1, HID_KEY_M             }, /* 0x4D M         */ \
+    {1, HID_KEY_N             }, /* 0x4E N         */ \
+    {1, HID_KEY_O             }, /* 0x4F O         */ \
+    {1, HID_KEY_P             }, /* 0x50 P         */ \
+    {1, HID_KEY_Q             }, /* 0x51 Q         */ \
+    {1, HID_KEY_R             }, /* 0x52 R         */ \
+    {1, HID_KEY_S             }, /* 0x53 S         */ \
+    {1, HID_KEY_T             }, /* 0x55 T         */ \
+    {1, HID_KEY_U             }, /* 0x55 U         */ \
+    {1, HID_KEY_V             }, /* 0x56 V         */ \
+    {1, HID_KEY_W             }, /* 0x57 W         */ \
+    {1, HID_KEY_X             }, /* 0x58 X         */ \
+    {1, HID_KEY_Y             }, /* 0x59 Y         */ \
+    {1, HID_KEY_Z             }, /* 0x5A Z         */ \
+    {0, HID_KEY_BRACKET_LEFT  }, /* 0x5B [         */ \
+    {0, HID_KEY_BACKSLASH     }, /* 0x5C '\'       */ \
+    {0, HID_KEY_BRACKET_RIGHT }, /* 0x5D ]         */ \
+    {1, HID_KEY_6             }, /* 0x5E ^         */ \
+    {1, HID_KEY_MINUS         }, /* 0x5F _         */ \
+                                                      \
+    {0, HID_KEY_GRAVE         }, /* 0x60 `         */ \
+    {0, HID_KEY_A             }, /* 0x61 a         */ \
+    {0, HID_KEY_B             }, /* 0x62 b         */ \
+    {0, HID_KEY_C             }, /* 0x63 c         */ \
+    {0, HID_KEY_D             }, /* 0x66 d         */ \
+    {0, HID_KEY_E             }, /* 0x65 e         */ \
+    {0, HID_KEY_F             }, /* 0x66 f         */ \
+    {0, HID_KEY_G             }, /* 0x67 g         */ \
+    {0, HID_KEY_H             }, /* 0x68 h         */ \
+    {0, HID_KEY_I             }, /* 0x69 i         */ \
+    {0, HID_KEY_J             }, /* 0x6A j         */ \
+    {0, HID_KEY_K             }, /* 0x6B k         */ \
+    {0, HID_KEY_L             }, /* 0x6C l         */ \
+    {0, HID_KEY_M             }, /* 0x6D m         */ \
+    {0, HID_KEY_N             }, /* 0x6E n         */ \
+    {0, HID_KEY_O             }, /* 0x6F o         */ \
+    {0, HID_KEY_P             }, /* 0x70 p         */ \
+    {0, HID_KEY_Q             }, /* 0x71 q         */ \
+    {0, HID_KEY_R             }, /* 0x72 r         */ \
+    {0, HID_KEY_S             }, /* 0x73 s         */ \
+    {0, HID_KEY_T             }, /* 0x75 t         */ \
+    {0, HID_KEY_U             }, /* 0x75 u         */ \
+    {0, HID_KEY_V             }, /* 0x76 v         */ \
+    {0, HID_KEY_W             }, /* 0x77 w         */ \
+    {0, HID_KEY_X             }, /* 0x78 x         */ \
+    {0, HID_KEY_Y             }, /* 0x79 y         */ \
+    {0, HID_KEY_Z             }, /* 0x7A z         */ \
+    {1, HID_KEY_BRACKET_LEFT  }, /* 0x7B {         */ \
+    {1, HID_KEY_BACKSLASH     }, /* 0x7C |         */ \
+    {1, HID_KEY_BRACKET_RIGHT }, /* 0x7D }         */ \
+    {1, HID_KEY_GRAVE         }, /* 0x7E ~         */ \
+    {0, HID_KEY_DELETE        }  /* 0x7F Delete    */ \
 
 //--------------------------------------------------------------------+
 // Internal Class Driver API

+ 4 - 2
src/class/msc/msc_device.c

@@ -163,8 +163,10 @@ bool mscd_control_request(uint8_t rhport, tusb_control_request_t const * p_reque
 
     case MSC_REQ_GET_MAX_LUN:
     {
-      // returned MAX LUN is minus 1 by specs
-      uint8_t maxlun = CFG_TUD_MSC_MAXLUN-1;
+      // MAX LUN is minus 1 by specs
+      uint8_t maxlun = 0;
+      if (tud_msc_maxlun_cb) maxlun = tud_msc_maxlun_cb() -1;
+
       usbd_control_xfer(rhport, p_request, &maxlun, 1);
     }
     break;

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

@@ -40,12 +40,6 @@
 //--------------------------------------------------------------------+
 TU_VERIFY_STATIC(CFG_TUD_MSC_BUFSIZE < UINT16_MAX, "Size is not correct");
 
-#ifndef CFG_TUD_MSC_MAXLUN
-  #define CFG_TUD_MSC_MAXLUN 1
-#elif CFG_TUD_MSC_MAXLUN == 0 || CFG_TUD_MSC_MAXLUN > 16
-  #error MSC Device: Incorrect setting of MAX LUN
-#endif
-
 #ifndef CFG_TUD_MSC_BUFSIZE
   #error CFG_TUD_MSC_BUFSIZE must be defined, value of a block size should work well, the more the better
 #endif
@@ -134,6 +128,9 @@ int32_t tud_msc_scsi_cb (uint8_t lun, uint8_t const scsi_cmd[16], void* buffer,
 
 /*------------- Optional callbacks -------------*/
 
+// Invoked to determine max LUN
+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);
 

+ 5 - 3
tools/build_all.py

@@ -4,8 +4,10 @@ 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"]
 
-for board in all_boards:
-    subprocess.run("make -j2 -C examples/device/cdc_msc_hid BOARD={} clean".format(board), shell=True)
-    subprocess.run("make -j2 -C examples/device/cdc_msc_hid BOARD={} all".format(board), shell=True)
+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)