From 19c0789107038d74220b9e2ac791675e58011e8e Mon Sep 17 00:00:00 2001
From: HASUMI Hitoshi <hasumikin@gmail.com>
Date: Wed, 8 Sep 2021 00:33:40 +0900
Subject: [PATCH 01/10] WIP

---
 CMakeLists.txt                 |  30 ++-
 include/tusb_config.h          |  15 +-
 src/main.c                     |   5 +-
 src/msc_disk.c                 | 418 +++++++++++++++++++++++++++++++++
 src/msc_disk.h                 |   9 +
 src/ruby/lib/keyboard.rb       |   4 +
 src/ruby/lib/rgb.rb            |   3 +-
 src/ruby/lib/rgb_task.rb       |   2 +
 src/ruby/lib/rotary_encoder.rb |   3 +-
 src/ruby/lib/tud.rb            |  11 +-
 src/ruby/sig/object.rbs        |   1 +
 src/usb.c                      |  40 ++--
 src/usb_descriptors.c          | 257 ++++++++++++++++++++
 src/version.h                  |   5 +
 src/version.h.in               |   5 +
 15 files changed, 774 insertions(+), 34 deletions(-)
 create mode 100644 src/msc_disk.c
 create mode 100644 src/msc_disk.h
 create mode 100644 src/usb_descriptors.c
 create mode 100644 src/version.h
 create mode 100644 src/version.h.in

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 26bfe9bb..981e045b 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -11,12 +11,25 @@ add_definitions(-DMRBC_USE_HAL_RP2040 -DMRBC_REQUIRE_32BIT_ALIGNMENT -DMAX_REGS_
 # initialize the Raspberry Pi Pico SDK
 pico_sdk_init()
 
-# rest of your project
+#####################################################
+# project specific configuration from here
+
+execute_process (COMMAND date +%Y%m%d OUTPUT_VARIABLE CMAKE_BUILDDATE OUTPUT_STRIP_TRAILING_WHITESPACE)
+execute_process (COMMAND git rev-parse --short HEAD OUTPUT_VARIABLE CMAKE_REVISION OUTPUT_STRIP_TRAILING_WHITESPACE)
+set (PRK_VERSION   0.9.0)
+set (PRK_BUILDDATE ${CMAKE_BUILDDATE})
+set (PRK_REVISION  ${CMAKE_REVISION})
+configure_file ("${CMAKE_CURRENT_SOURCE_DIR}/src/version.h.in" "${CMAKE_CURRENT_SOURCE_DIR}/src/version.h")
+
+set(PROJECT "prk_firmware-${PRK_VERSION}-${PRK_BUILDDATE}-${PRK_REVISION}")
+#set(PROJECT "prk_firmware")  # for test
 
 add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/lib)
 
-add_executable(prk_firmware
+add_executable(${PROJECT}
   src/main.c
+  src/msc_disk.c
+  src/usb_descriptors.c
   src/gpio.c
   src/usb.c
   src/uart.c
@@ -25,8 +38,8 @@ add_executable(prk_firmware
   lib/picoruby/cli/sandbox.c
 )
 
-pico_generate_pio_header(prk_firmware ${CMAKE_CURRENT_LIST_DIR}/src/uart_tx.pio)
-pico_generate_pio_header(prk_firmware ${CMAKE_CURRENT_LIST_DIR}/src/ws2812.pio)
+pico_generate_pio_header(${PROJECT} ${CMAKE_CURRENT_LIST_DIR}/src/uart_tx.pio)
+pico_generate_pio_header(${PROJECT} ${CMAKE_CURRENT_LIST_DIR}/src/ws2812.pio)
 
 set(RBC ${CMAKE_CURRENT_SOURCE_DIR}/lib/picoruby/build/bin/host-production/alloc_libc/picorbc)
 #set(RBC RBENV_VERSION=mruby-3.0.0 mrbc)
@@ -59,13 +72,13 @@ add_dependencies(ruby
   steep
 )
 
-add_dependencies(prk_firmware
+add_dependencies(${PROJECT}
   picoruby
   ruby
   ruby_2
 )
 
-target_link_libraries(prk_firmware
+target_link_libraries(${PROJECT}
   pico_stdlib
   tinyusb_device
   tinyusb_board
@@ -73,9 +86,10 @@ target_link_libraries(prk_firmware
   hardware_pio
   hardware_pwm
   pico_multicore
+  hardware_flash
 )
 
-include_directories(prk_firmware
+include_directories(${PROJECT}
   PRIVATE
     ${CMAKE_CURRENT_LIST_DIR}
     ${CMAKE_CURRENT_LIST_DIR}/include
@@ -83,4 +97,4 @@ include_directories(prk_firmware
 )
 
 # create map/bin/hex/uf2 file in addition to ELF.
-pico_add_extra_outputs(prk_firmware)
+pico_add_extra_outputs(${PROJECT})
diff --git a/include/tusb_config.h b/include/tusb_config.h
index 1bb0b443..b73bc7d6 100644
--- a/include/tusb_config.h
+++ b/include/tusb_config.h
@@ -94,8 +94,8 @@
 #endif
 
 //------------- CLASS -------------//
-#define CFG_TUD_CDC               0
-#define CFG_TUD_MSC               0
+#define CFG_TUD_CDC               1
+#define CFG_TUD_MSC               1
 #define CFG_TUD_HID               1
 #define CFG_TUD_MIDI              0
 #define CFG_TUD_VENDOR            0
@@ -103,6 +103,17 @@
 // HID buffer size Should be sufficient to hold ID (if any) + Data
 #define CFG_TUD_HID_EP_BUFSIZE    64
 
+// It is normaly 512, but we use 4096 to avoid cache coherency problem
+// on writing flash
+#define CFG_TUD_MSC_EP_BUFSIZE    512
+
+// CDC FIFO size of TX and RX
+#define CFG_TUD_CDC_RX_BUFSIZE   (TUD_OPT_HIGH_SPEED ? 512 : 64)
+#define CFG_TUD_CDC_TX_BUFSIZE   (TUD_OPT_HIGH_SPEED ? 512 : 64)
+
+// CDC Endpoint transfer buffer size, more is faster
+#define CFG_TUD_CDC_EP_BUFSIZE   (TUD_OPT_HIGH_SPEED ? 512 : 64)
+
 #ifdef __cplusplus
  }
 #endif
diff --git a/src/main.c b/src/main.c
index cbeb1508..84c7f707 100644
--- a/src/main.c
+++ b/src/main.c
@@ -9,6 +9,7 @@
 #include "hardware/clocks.h"
 
 /* mrbc_class */
+#include "msc_disk.h"
 #include "gpio.h"
 #include "usb.h"
 #include "uart.h"
@@ -46,7 +47,7 @@ c_rand(mrb_vm *vm, mrb_value *v, int argc)
   SET_INT_RETURN(rand());
 }
 
-#define MEMORY_SIZE (1024*220)
+#define MEMORY_SIZE (1024*200)
 
 static uint8_t memory_pool[MEMORY_SIZE];
 
@@ -73,9 +74,11 @@ int main() {
   stdio_init_all();
   board_init();
   tusb_init();
+  msc_init();
   mrbc_init(memory_pool, MEMORY_SIZE);
   mrbc_define_method(0, mrbc_class_object, "board_millis", c_board_millis);
   mrbc_define_method(0, mrbc_class_object, "rand",         c_rand);
+  MSC_INIT();
   GPIO_INIT();
   USB_INIT();
   UART_INIT();
diff --git a/src/msc_disk.c b/src/msc_disk.c
new file mode 100644
index 00000000..ad7c418b
--- /dev/null
+++ b/src/msc_disk.c
@@ -0,0 +1,418 @@
+/* 
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach (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
+
+#include "hardware/flash.h"
+#include "hardware/sync.h"
+
+#include "msc_disk.h"
+
+#define FLASH_TARGET_OFFSET  0x00180000                       /* 1.5MB */
+#define FLASH_MMAP_ADDR      (XIP_BASE + FLASH_TARGET_OFFSET) /* 0x10180000 */
+#define SECTOR_SIZE          4096
+#define SECTOR_COUNT         128   /* 4096 * 128 = 512KB */
+
+// whether host does safe-eject
+static bool ejected = false;
+
+#include "version.h"
+
+#define PRK_DESCRIPTION \
+"PRK Firmware " PRK_VERSION " (" PRK_BUILDDATE " revision " PRK_REVISION ")"
+
+/* PRK_DESCRIPTION should be located at the beginning of README. see msc_init() */
+#define README_CONTENTS \
+PRK_DESCRIPTION "\n\nWelcome to PRK Firmware!\n\n\
+Usage:\n\
+- Drag and drop your `keymap.rb` into this directory\n\
+- Then, your keyboard will be automatically rebooted. That's all!\n\n\
+Notice:\n\
+- Make sure you always have a backup of your `keymap.rb`\n\
+  because upgrading prk_firmware-*.uf2 will remove it from flash\n\n\
+https://github.com/picoruby/prk_firmware\n"
+
+#define README_LENGTH (sizeof(README_CONTENTS) - 1)
+
+uint8_t msc_disk[4][SECTOR_SIZE] =
+{
+  //------------- Block0: Boot Sector -------------//
+  // byte_per_sector    = SECTOR_SIZE; fat12_sector_num_16  = SECTOR_COUNT;
+  // 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 = "PRKFirmware";
+  {
+    0xEB, 0x3C, 0x90,       /*    BS_jmpBoot */
+    0x4D, 0x53, 0x44, 0x4F, 0x53, 0x35, 0x2E, 0x30,   /* BS_OEMName "MSDOS5.0" */
+    0x00, 0x10,             /* ** BPB_BytePerSec ** */
+    0x01,                   /*    BPB_SecPerClus */
+    0x01, 0x00,             /*    BPB_RsvdSecCnt */
+    0x01,                   /*    BPB_NumFATs */
+    0x80, 0x00,             /* ** BPB_RootEntCnt ** */
+    0x00, 0x01,             /* ** BPB_TotSec16 ** */
+    0xF8,                   /*    BPB_Media  */
+    0x01, 0x00,             /*    BPB_FATSz16 */
+    0x01, 0x00,             /*    BPB_SecPerTrk */
+    0x01, 0x00,             /*    BPB_NumHeads */
+    0x00, 0x00, 0x00, 0x00, /*    BPB_HiddSec */
+    0x00, 0x00, 0x00, 0x00, /*    BPB_TotSec32 */
+    0x80, 0x00, 0x29, 0x34, 0x12, 0x00, 0x00, 'P' , 'R' , 'K' , 'F' , 'i' ,
+    'r' , 'm' , 'w' , 'a' , 'r' , 'e' , 0x46, 0x41, 0x54, 0x31, 0x32, 0x20, 0x20, 0x20, 0x00, 0x00,
+    // Zerofill until BS_BootSign (0xAA55 at offset 510)
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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
+    'P' , 'R' , 'K' , 'F' , 'i' , 'r' , 'm' , 'w' , 'a' , 'r' , 'e' , 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,
+    // readme's files size (4 Bytes little endian)
+    README_LENGTH & 0xFF,
+    README_LENGTH >> 8 & 0xFF,
+    README_LENGTH >> 16 & 0xFF,
+    README_LENGTH >> 24
+  },
+
+  //------------- Block3: Readme Content -------------//
+  README_CONTENTS
+};
+
+// Invoked when received SCSI_CMD_INQUIRY
+// Application fill vendor id, product id and revision with string up to 8, 16, 4 characters respectively
+void tud_msc_inquiry_cb(uint8_t lun, uint8_t vendor_id[8], uint8_t product_id[16], uint8_t product_rev[4])
+{
+  (void) lun;
+
+  const char vid[] = "TinyUSB";
+  const char pid[] = "Mass Storage";
+  const char rev[] = "1.0";
+
+  memcpy(vendor_id  , vid, strlen(vid));
+  memcpy(product_id , pid, strlen(pid));
+  memcpy(product_rev, rev, strlen(rev));
+}
+
+// Invoked when received Test Unit Ready command.
+// return true allowing host to read/write this LUN e.g SD card inserted
+bool tud_msc_test_unit_ready_cb(uint8_t lun)
+{
+  (void) lun;
+
+  // RAM disk is ready until ejected
+  if (ejected) {
+    tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x3a, 0x00);
+    return false;
+  }
+
+  return true;
+}
+
+// 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)
+{
+  (void) lun;
+
+  *block_count = SECTOR_COUNT;
+  *block_size  = SECTOR_SIZE;
+}
+
+// Invoked when received Start Stop Unit command
+// - Start = 0 : stopped power mode, if load_eject = 1 : unload disk storage
+// - Start = 1 : active mode, if load_eject = 1 : load disk storage
+bool tud_msc_start_stop_cb(uint8_t lun, uint8_t power_condition, bool start, bool load_eject)
+{
+  (void) lun;
+  (void) power_condition;
+
+  if ( load_eject )
+  {
+    if (start)
+    {
+      // load disk storage
+      ejected = false;
+    }else
+    {
+      // unload disk storage
+      ejected = true;
+    }
+  }
+
+  return true;
+}
+
+// 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)
+{
+  (void) lun;
+  // out of ramdisk
+  if ( lba >= SECTOR_COUNT ) return -1;
+  memcpy(buffer, (void *)(FLASH_MMAP_ADDR + lba * SECTOR_SIZE + offset), bufsize);
+  return bufsize;
+}
+
+bool tud_msc_is_writable_cb (uint8_t lun)
+{
+  (void) lun;
+#ifdef PICORUBY_MSC_READONLY
+  return false;
+#else
+  return true;
+#endif
+}
+
+// Callback invoked when received WRITE10 command.
+// Process data in buffer to disk's storage and return number of written bytes
+int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset, uint8_t* buffer, uint32_t bufsize)
+/*
+ * TODO: Cache coherency problem can be avoided if bufsize == SECTOR_SIZE(4094)
+ */
+{
+  (void) lun;
+  // out of ramdisk
+  if ( lba >= SECTOR_COUNT ) return -1;
+  uint32_t ints = save_and_disable_interrupts();
+  if (offset == 0) {
+    flash_range_erase(FLASH_TARGET_OFFSET + lba * SECTOR_SIZE, SECTOR_SIZE);
+  }
+  flash_range_program(FLASH_TARGET_OFFSET + lba * SECTOR_SIZE + offset, buffer, bufsize);
+  restore_interrupts(ints);
+  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;
+  int32_t resplen = 0;
+
+  // most scsi handled is input
+  bool in_xfer = true;
+
+  switch (scsi_cmd[0])
+  {
+    case SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL:
+      // Host is about to read/write etc ... better not to disconnect disk
+      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;
+}
+
+void msc_init(void)
+{
+#ifndef FORCE_FORMAT_FLASH
+  if (
+      strncmp(
+        (void *)(FLASH_MMAP_ADDR + SECTOR_SIZE * 3),
+        PRK_DESCRIPTION,
+        sizeof(PRK_DESCRIPTION) - 1 /* Skips null term */
+      )
+     )
+#endif
+  {
+    /* These functions know XIP_BASE */
+    flash_range_erase(FLASH_TARGET_OFFSET, SECTOR_SIZE * 4);
+    flash_range_program(FLASH_TARGET_OFFSET, msc_disk[0], SECTOR_SIZE * 4);
+  }
+}
+
+/*
+ * Directory entry in FAT Volume
+ * (FAT data is little endian)
+ */
+typedef struct dir_ent {
+  char     Name[11];
+  uint8_t  Attr;
+  uint8_t  NTRes;
+  uint8_t  CrtTimeTenth;
+  char     CrtTime[2];
+  char     CrtDate[2];
+  char     LastAccDate[2];
+  uint16_t FstClusHI;
+  char     WrtTime[2];
+  char     WrtDate[2];
+  uint16_t FstClusLO;
+  uint32_t FileSize;
+} DirEnt;
+
+/*
+ * Only works in top directory
+ * Only works if SFN (short file name)
+ */
+void
+msc_findDirEnt(const char *filename, DirEnt *entry)
+{
+  void *addr;
+  for (
+        addr = (void *)(FLASH_MMAP_ADDR + (SECTOR_SIZE * 2) + 32);
+        (uint32_t)addr < FLASH_MMAP_ADDR + SECTOR_SIZE * SECTOR_COUNT;
+        addr += 32
+      )
+  {
+    entry->Name[0] = '\0';
+    memcpy(entry, addr, 32);
+    if (entry->Name[0] == 0xe5) continue;
+    if (entry->Name[0] == '\0') return;
+    if (strncmp(filename, entry->Name, 11) == 0 && entry->Attr == 0x20) return;
+  }
+}
+
+//volatile char ppp;
+void
+tud_msc_write10_complete_cb(uint8_t lun)
+{
+  (void)lun;
+//  DirEnt entry;
+//  msc_findDirEnt("README  TXT", &entry);
+//  if (entry.Name[0] != '\0') {
+//    const char *program = (const char *)(FLASH_MMAP_ADDR + SECTOR_SIZE * (1 + entry.FstClusLO));
+//    ppp = program[0];
+//  }
+}
+
+//--------------------------------------------------------------------+
+// USB CDC
+//--------------------------------------------------------------------+
+void
+c_cdc_task(mrb_vm *vm, mrb_value *v, int argc)
+{
+  // connected() check for DTR bit
+  // Most but not all terminal client set this when making connection
+  // if ( tud_cdc_connected() )
+  {
+    // connected and there are data available
+    if ( tud_cdc_available() )
+    {
+      // read datas
+      char buf[64];
+      uint32_t count = tud_cdc_read(buf, sizeof(buf));
+      (void) count;
+
+      // Echo back
+      // Note: Skip echo by commenting out write() and write_flush()
+      // for throughput test e.g
+      //    $ dd if=/dev/zero of=/dev/ttyACM0 count=10000
+      tud_cdc_write(buf, count);
+      tud_cdc_write_flush();
+    }
+  }
+}
+
+// Invoked when cdc when line state changed e.g connected/disconnected
+void tud_cdc_line_state_cb(uint8_t itf, bool dtr, bool rts)
+{
+  (void) itf;
+  (void) rts;
+
+  // TODO set some indicator
+  if ( dtr )
+  {
+    // Terminal connected
+  }else
+  {
+    // Terminal disconnected
+  }
+}
+
+// Invoked when CDC interface received data from host
+void tud_cdc_rx_cb(uint8_t itf)
+{
+  (void) itf;
+}
+
+#endif
diff --git a/src/msc_disk.h b/src/msc_disk.h
new file mode 100644
index 00000000..8e810e69
--- /dev/null
+++ b/src/msc_disk.h
@@ -0,0 +1,9 @@
+/* mruby/c VM */
+#include <mrubyc.h>
+
+void msc_init(void);
+void c_cdc_task(mrb_vm *vm, mrb_value *v, int argc);
+
+#define MSC_INIT() do { \
+  mrbc_define_method(0, mrbc_class_object, "cdc_task",  c_cdc_task);  \
+} while (0)
diff --git a/src/ruby/lib/keyboard.rb b/src/ruby/lib/keyboard.rb
index a2ebca7e..e17b4d41 100644
--- a/src/ruby/lib/keyboard.rb
+++ b/src/ruby/lib/keyboard.rb
@@ -335,6 +335,7 @@ class Keyboard
   letter = nil
 
   def initialize
+    puts "Initializing Keyboard ..."
     # mruby/c VM doesn't work with a CONSTANT to make another CONSTANT
     # steep doesn't allow dynamic assignment of CONSTANT
     @SHIFT_LETTER_THRESHOLD_A    = LETTER.index('A').to_i
@@ -624,6 +625,8 @@ def send_key(symbol)
   #   Please refrain from "refactoring" for a while.
   # **************************************************************
   def start!
+    puts "Starting keyboard task ..."
+
     start_features
     @keycodes = Array.new
     # To avoid unintentional report on startup
@@ -871,6 +874,7 @@ def unlock_layer
   end
 
   def macro(text, opts = [:ENTER])
+    puts "macro: #{text}"
     prev_c = ""
     text.to_s.each_char do |c|
       index = LETTER.index(c)
diff --git a/src/ruby/lib/rgb.rb b/src/ruby/lib/rgb.rb
index 58f27acf..def899d2 100644
--- a/src/ruby/lib/rgb.rb
+++ b/src/ruby/lib/rgb.rb
@@ -1,5 +1,6 @@
 class RGB
-  def initialize(pin, underglow_size, backlight_size, is_rgbw)
+ def initialize(pin, underglow_size, backlight_size, is_rgbw)
+    puts "Initializing RGB ..."
     @fifo = Array.new
     # TODO: @underglow_size, @backlight_size
     @pixel_size = underglow_size + backlight_size
diff --git a/src/ruby/lib/rgb_task.rb b/src/ruby/lib/rgb_task.rb
index bd0fb2db..d1d8c13d 100644
--- a/src/ruby/lib/rgb_task.rb
+++ b/src/ruby/lib/rgb_task.rb
@@ -14,6 +14,8 @@
   hue = 0
 end
 
+puts "Starting rgb task ..."
+
 while true
   if $rgb.key?
     case $rgb.action
diff --git a/src/ruby/lib/rotary_encoder.rb b/src/ruby/lib/rotary_encoder.rb
index d212d508..6c0d0b17 100644
--- a/src/ruby/lib/rotary_encoder.rb
+++ b/src/ruby/lib/rotary_encoder.rb
@@ -1,5 +1,6 @@
 class RotaryEncoder
-  def initialize(pin_a, pin_b)
+ def initialize(pin_a, pin_b)
+    puts "Initializing RotaryEncoder ..."
     @pin_a = pin_a
     @pin_b = pin_b
     @rotation = 0
diff --git a/src/ruby/lib/tud.rb b/src/ruby/lib/tud.rb
index bb9e38cc..4c06522c 100644
--- a/src/ruby/lib/tud.rb
+++ b/src/ruby/lib/tud.rb
@@ -1,4 +1,13 @@
+print "\033[2J"   # clear all
+print "\033[1;1H" # home
+puts "Welcome to PRK Firmware"
+puts
+puts "Starting tud task ..."
+
 while true
   tud_task
-  $encoders.each { |encoder| encoder.read } unless $encoders.empty?
+  cdc_task
+  if $encoders && !$encoders.empty?
+    $encoders.each { |encoder| encoder.read }
+  end
 end
diff --git a/src/ruby/sig/object.rbs b/src/ruby/sig/object.rbs
index 17b2778a..6571a6dc 100644
--- a/src/ruby/sig/object.rbs
+++ b/src/ruby/sig/object.rbs
@@ -10,6 +10,7 @@ class Object
 
   # Raspi
   def board_millis : -> Integer
+  def cdc_task : -> void
   def tud_task : -> void
   def tud_mounted? : () -> bool
   def gpio_init : (Integer) -> void
diff --git a/src/usb.c b/src/usb.c
index b5fe64d4..160cdfc0 100644
--- a/src/usb.c
+++ b/src/usb.c
@@ -72,26 +72,26 @@ const uint16_t string_desc_product[] = { // Index: 1
   'P', 'R', 'K', 'f', 'i', 'r', 'm'
 };
 
-uint8_t const *tud_descriptor_device_cb(void) {
-  return device_desc;
-}
-uint8_t const *tud_descriptor_configuration_cb(uint8_t index) {
-  return conf_desc;
-}
-uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid) {
-  uint16_t const *ret = NULL;
-  switch(index) {
-    case 0:
-      ret = string_desc_lang;
-      break;
-    case 1:
-      ret = string_desc_product;
-      break;
-    default:
-      break;
-  }
-  return ret;
-}
+//uint8_t const *tud_descriptor_device_cb(void) {
+//  return device_desc;
+//}
+//uint8_t const *tud_descriptor_configuration_cb(uint8_t index) {
+//  return conf_desc;
+//}
+//uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid) {
+//  uint16_t const *ret = NULL;
+//  switch(index) {
+//    case 0:
+//      ret = string_desc_lang;
+//      break;
+//    case 1:
+//      ret = string_desc_product;
+//      break;
+//    default:
+//      break;
+//  }
+//  return ret;
+//}
 
 
 uint8_t const *tud_hid_descriptor_report_cb(uint8_t instance) {
diff --git a/src/usb_descriptors.c b/src/usb_descriptors.c
new file mode 100644
index 00000000..9702a443
--- /dev/null
+++ b/src/usb_descriptors.c
@@ -0,0 +1,257 @@
+/* 
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach (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           (0x8000 | _PID_MAP(CDC, 0) | _PID_MAP(MSC, 1) | _PID_MAP(HID, 2) | \
+                           _PID_MAP(MIDI, 3) | _PID_MAP(VENDOR, 4) )
+
+//--------------------------------------------------------------------+
+// Device Descriptors
+//--------------------------------------------------------------------+
+tusb_desc_device_t const desc_device =
+{
+    .bLength            = sizeof(tusb_desc_device_t),
+    .bDescriptorType    = TUSB_DESC_DEVICE,
+    .bcdUSB             = 0x0200,
+
+    // Use Interface Association Descriptor (IAD) for CDC
+    // As required by USB Specs IAD's subclass must be common class (2) and protocol must be IAD (1)
+    .bDeviceClass       = TUSB_CLASS_MISC,
+    .bDeviceSubClass    = MISC_SUBCLASS_COMMON,
+    .bDeviceProtocol    = MISC_PROTOCOL_IAD,
+
+    .bMaxPacketSize0    = CFG_TUD_ENDPOINT0_SIZE,
+
+    .idVendor           = 0xCafe,
+    .idProduct          = USB_PID,
+    .bcdDevice          = 0x0100,
+
+    .iManufacturer      = 0x01,
+    .iProduct           = 0x02,
+    .iSerialNumber      = 0x03,
+
+    .bNumConfigurations = 0x01
+};
+
+// Invoked when received GET DEVICE DESCRIPTOR
+// Application return pointer to descriptor
+uint8_t const * tud_descriptor_device_cb(void)
+{
+  return (uint8_t const *) &desc_device;
+}
+
+//--------------------------------------------------------------------+
+// Configuration Descriptor
+//--------------------------------------------------------------------+
+
+enum
+{
+  ITF_NUM_CDC = 0,
+  ITF_NUM_CDC_DATA,
+  ITF_NUM_MSC,
+//  ITF_NUM_HID,
+  ITF_NUM_TOTAL
+};
+
+#define CONFIG_TOTAL_LEN    (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN + TUD_MSC_DESC_LEN)
+//#define CONFIG_TOTAL_LEN    (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN + TUD_MSC_DESC_LEN + TUD_HID_DESC_LEN)
+//#define CONFIG_TOTAL_LEN    (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN + TUD_MSC_DESC_LEN + TUD_HID_INOUT_DESC_LEN)
+
+#if CFG_TUSB_MCU == OPT_MCU_LPC175X_6X || CFG_TUSB_MCU == OPT_MCU_LPC177X_8X || CFG_TUSB_MCU == OPT_MCU_LPC40XX
+  // LPC 17xx and 40xx endpoint type (bulk/interrupt/iso) are fixed by its number
+  // 0 control, 1 In, 2 Bulk, 3 Iso, 4 In, 5 Bulk etc ...
+  #define EPNUM_CDC_NOTIF   0x81
+  #define EPNUM_CDC_OUT     0x02
+  #define EPNUM_CDC_IN      0x82
+
+  #define EPNUM_MSC_OUT     0x05
+  #define EPNUM_MSC_IN      0x85
+
+#elif CFG_TUSB_MCU == OPT_MCU_SAMG  || CFG_TUSB_MCU ==  OPT_MCU_SAMX7X
+  // SAMG & SAME70 don't support a same endpoint number with different direction IN and OUT
+  //    e.g EP1 OUT & EP1 IN cannot exist together
+  #define EPNUM_CDC_NOTIF   0x81
+  #define EPNUM_CDC_OUT     0x02
+  #define EPNUM_CDC_IN      0x83
+
+  #define EPNUM_MSC_OUT     0x04
+  #define EPNUM_MSC_IN      0x85
+
+#elif CFG_TUSB_MCU == OPT_MCU_CXD56
+  // CXD56 doesn't support a same endpoint number with different direction IN and OUT
+  //    e.g EP1 OUT & EP1 IN cannot exist together
+  // CXD56 USB driver has fixed endpoint type (bulk/interrupt/iso) and direction (IN/OUT) by its number
+  // 0 control (IN/OUT), 1 Bulk (IN), 2 Bulk (OUT), 3 In (IN), 4 Bulk (IN), 5 Bulk (OUT), 6 In (IN)
+  #define EPNUM_CDC_NOTIF   0x83
+  #define EPNUM_CDC_OUT     0x02
+  #define EPNUM_CDC_IN      0x81
+
+  #define EPNUM_MSC_OUT     0x05
+  #define EPNUM_MSC_IN      0x84
+
+#else
+  #define EPNUM_CDC_NOTIF   0x81
+  #define EPNUM_CDC_OUT     0x02
+  #define EPNUM_CDC_IN      0x82
+
+  #define EPNUM_MSC_OUT     0x03
+  #define EPNUM_MSC_IN      0x83
+
+#endif
+
+//enum {
+//    REPORT_ID_KEYBOARD = 1,
+//    REPORT_ID_MOUSE
+//};
+enum
+{
+  REPORT_ID_KEYBOARD = 1,
+  REPORT_ID_MOUSE,
+  REPORT_ID_CONSUMER_CONTROL,
+  REPORT_ID_GAMEPAD,
+  REPORT_ID_COUNT
+};
+
+#define EPNUM_HID   0x81
+
+uint8_t const desc_hid_report[] =
+{
+  TUD_HID_REPORT_DESC_KEYBOARD( HID_REPORT_ID(REPORT_ID_KEYBOARD         )),
+  TUD_HID_REPORT_DESC_MOUSE   ( HID_REPORT_ID(REPORT_ID_MOUSE            )),
+  TUD_HID_REPORT_DESC_CONSUMER( HID_REPORT_ID(REPORT_ID_CONSUMER_CONTROL )),
+  TUD_HID_REPORT_DESC_GAMEPAD ( HID_REPORT_ID(REPORT_ID_GAMEPAD          ))
+};
+uint8_t const desc_fs_configuration[] =
+{
+  // Config number, interface count, string index, total length, attribute, power in mA
+  TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100),
+
+  // Interface number, string index, EP notification address and size, EP data address (out, in) and size.
+  TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 4, EPNUM_CDC_NOTIF, 8, EPNUM_CDC_OUT, EPNUM_CDC_IN, 64),
+
+  // Interface number, string index, EP Out & EP In address, EP size
+  TUD_MSC_DESCRIPTOR(ITF_NUM_MSC, 5, EPNUM_MSC_OUT, EPNUM_MSC_IN, 64),
+
+  // Interface number, string index, protocol, report descriptor len, EP In address, size & polling interval
+  //TUD_HID_DESCRIPTOR(ITF_NUM_HID, 6, HID_ITF_PROTOCOL_NONE, sizeof(desc_hid_report), EPNUM_HID, CFG_TUD_HID_EP_BUFSIZE, 5)
+  //TUD_HID_INOUT_DESCRIPTOR(ITF_NUM_HID, 0, HID_ITF_PROTOCOL_NONE, sizeof(desc_hid_report), EPNUM_HID, 0x80 | EPNUM_HID, CFG_TUD_HID_EP_BUFSIZE, 10)
+};
+
+#if TUD_OPT_HIGH_SPEED
+uint8_t const desc_hs_configuration[] =
+{
+  // Config number, interface count, string index, total length, attribute, power in mA
+  TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100),
+
+  // Interface number, string index, EP notification address and size, EP data address (out, in) and size.
+  TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 4, EPNUM_CDC_NOTIF, 8, EPNUM_CDC_OUT, EPNUM_CDC_IN, 512),
+
+  // Interface number, string index, EP Out & EP In address, EP size
+  TUD_MSC_DESCRIPTOR(ITF_NUM_MSC, 5, EPNUM_MSC_OUT, EPNUM_MSC_IN, 512),
+
+  // Interface number, string index, protocol, report descriptor len, EP In address, size & polling interval
+  //TUD_HID_DESCRIPTOR(ITF_NUM_HID, 6, HID_ITF_PROTOCOL_NONE, sizeof(desc_hid_report), EPNUM_HID, CFG_TUD_HID_EP_BUFSIZE, 5)
+  //TUD_HID_INOUT_DESCRIPTOR(ITF_NUM_HID, 0, HID_ITF_PROTOCOL_NONE, sizeof(desc_hid_report), EPNUM_HID, 0x80 | EPNUM_HID, CFG_TUD_HID_EP_BUFSIZE, 10)
+};
+#endif
+
+
+// Invoked when received GET CONFIGURATION DESCRIPTOR
+// Application return pointer to descriptor
+// Descriptor contents must exist long enough for transfer to complete
+uint8_t const * tud_descriptor_configuration_cb(uint8_t index)
+{
+  (void) index; // for multiple configurations
+
+#if TUD_OPT_HIGH_SPEED
+  // Although we are highspeed, host may be fullspeed.
+  return (tud_speed_get() == TUSB_SPEED_HIGH) ?  desc_hs_configuration : desc_fs_configuration;
+#else
+  return desc_fs_configuration;
+#endif
+}
+
+//--------------------------------------------------------------------+
+// String Descriptors
+//--------------------------------------------------------------------+
+
+// array of pointer to string descriptors
+char const* string_desc_arr [] =
+{
+  (const char[]) { 0x09, 0x04 }, // 0: is supported language is English (0x0409)
+  "Raspberry Pi",                // 1: Manufacturer
+  "RP2040",                      // 2: Product
+  "123456",                      // 3: Serials, should use chip ID
+  "TinyUSB CDC",                 // 4: CDC Interface
+  "TinyUSB MSC",                 // 5: MSC Interface
+};
+
+static uint16_t _desc_str[32];
+
+// Invoked when received GET STRING DESCRIPTOR request
+// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete
+uint16_t const* tud_descriptor_string_cb(uint8_t index, uint16_t langid)
+{
+  (void) langid;
+
+  uint8_t chr_count;
+
+  if ( index == 0)
+  {
+    memcpy(&_desc_str[1], string_desc_arr[0], 2);
+    chr_count = 1;
+  }else
+  {
+    // Note: the 0xEE index string is a Microsoft OS 1.0 Descriptors.
+    // https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-defined-usb-descriptors
+
+    if ( !(index < sizeof(string_desc_arr)/sizeof(string_desc_arr[0])) ) return NULL;
+
+    const char* str = string_desc_arr[index];
+
+    // Cap at max char
+    chr_count = strlen(str);
+    if ( chr_count > 31 ) chr_count = 31;
+
+    // Convert ASCII string into UTF-16
+    for(uint8_t i=0; i<chr_count; i++)
+    {
+      _desc_str[1+i] = str[i];
+    }
+  }
+
+  // first byte is length (including header), second byte is string type
+  _desc_str[0] = (TUSB_DESC_STRING << 8 ) | (2*chr_count + 2);
+
+  return _desc_str;
+}
diff --git a/src/version.h b/src/version.h
new file mode 100644
index 00000000..1f235afb
--- /dev/null
+++ b/src/version.h
@@ -0,0 +1,5 @@
+#pragma once
+
+#define PRK_VERSION   "0.9.0"
+#define PRK_BUILDDATE "20210907"
+#define PRK_REVISION  "e8ee115"
diff --git a/src/version.h.in b/src/version.h.in
new file mode 100644
index 00000000..5c8c7389
--- /dev/null
+++ b/src/version.h.in
@@ -0,0 +1,5 @@
+#pragma once
+
+#define PRK_VERSION   "@PRK_VERSION@"
+#define PRK_BUILDDATE "@PRK_BUILDDATE@"
+#define PRK_REVISION  "@PRK_REVISION@"

From 3e3bf2b053362b1793675ef4e6f199e041f09bd4 Mon Sep 17 00:00:00 2001
From: HASUMI Hitoshi <hasumikin@gmail.com>
Date: Wed, 8 Sep 2021 10:17:42 +0900
Subject: [PATCH 02/10] =?UTF-8?q?MSC=20and=20HID=20work=20together!=20(onl?=
 =?UTF-8?q?y=20on=20Debug=20=F0=9F=98=95)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/msc_disk.c        |  2 +-
 src/usb_descriptors.c | 17 +++++++----------
 2 files changed, 8 insertions(+), 11 deletions(-)

diff --git a/src/msc_disk.c b/src/msc_disk.c
index ad7c418b..46c42537 100644
--- a/src/msc_disk.c
+++ b/src/msc_disk.c
@@ -305,7 +305,7 @@ void msc_init(void)
      )
 #endif
   {
-    /* These functions know XIP_BASE */
+    /* These functions know XIP_BASE so you just have to give them FLASH_TARGET_OFFSET */
     flash_range_erase(FLASH_TARGET_OFFSET, SECTOR_SIZE * 4);
     flash_range_program(FLASH_TARGET_OFFSET, msc_disk[0], SECTOR_SIZE * 4);
   }
diff --git a/src/usb_descriptors.c b/src/usb_descriptors.c
index 9702a443..8c3c0436 100644
--- a/src/usb_descriptors.c
+++ b/src/usb_descriptors.c
@@ -79,13 +79,11 @@ enum
   ITF_NUM_CDC = 0,
   ITF_NUM_CDC_DATA,
   ITF_NUM_MSC,
-//  ITF_NUM_HID,
+  ITF_NUM_HID,
   ITF_NUM_TOTAL
 };
 
-#define CONFIG_TOTAL_LEN    (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN + TUD_MSC_DESC_LEN)
-//#define CONFIG_TOTAL_LEN    (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN + TUD_MSC_DESC_LEN + TUD_HID_DESC_LEN)
-//#define CONFIG_TOTAL_LEN    (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN + TUD_MSC_DESC_LEN + TUD_HID_INOUT_DESC_LEN)
+#define CONFIG_TOTAL_LEN    (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN + TUD_MSC_DESC_LEN + TUD_HID_INOUT_DESC_LEN)
 
 #if CFG_TUSB_MCU == OPT_MCU_LPC175X_6X || CFG_TUSB_MCU == OPT_MCU_LPC177X_8X || CFG_TUSB_MCU == OPT_MCU_LPC40XX
   // LPC 17xx and 40xx endpoint type (bulk/interrupt/iso) are fixed by its number
@@ -142,15 +140,15 @@ enum
   REPORT_ID_COUNT
 };
 
-#define EPNUM_HID   0x81
+#define EPNUM_HID_IN    0x04
+#define EPNUM_HID_OUT   0x84
 
 uint8_t const desc_hid_report[] =
 {
   TUD_HID_REPORT_DESC_KEYBOARD( HID_REPORT_ID(REPORT_ID_KEYBOARD         )),
   TUD_HID_REPORT_DESC_MOUSE   ( HID_REPORT_ID(REPORT_ID_MOUSE            )),
-  TUD_HID_REPORT_DESC_CONSUMER( HID_REPORT_ID(REPORT_ID_CONSUMER_CONTROL )),
-  TUD_HID_REPORT_DESC_GAMEPAD ( HID_REPORT_ID(REPORT_ID_GAMEPAD          ))
 };
+
 uint8_t const desc_fs_configuration[] =
 {
   // Config number, interface count, string index, total length, attribute, power in mA
@@ -163,8 +161,7 @@ uint8_t const desc_fs_configuration[] =
   TUD_MSC_DESCRIPTOR(ITF_NUM_MSC, 5, EPNUM_MSC_OUT, EPNUM_MSC_IN, 64),
 
   // Interface number, string index, protocol, report descriptor len, EP In address, size & polling interval
-  //TUD_HID_DESCRIPTOR(ITF_NUM_HID, 6, HID_ITF_PROTOCOL_NONE, sizeof(desc_hid_report), EPNUM_HID, CFG_TUD_HID_EP_BUFSIZE, 5)
-  //TUD_HID_INOUT_DESCRIPTOR(ITF_NUM_HID, 0, HID_ITF_PROTOCOL_NONE, sizeof(desc_hid_report), EPNUM_HID, 0x80 | EPNUM_HID, CFG_TUD_HID_EP_BUFSIZE, 10)
+  TUD_HID_INOUT_DESCRIPTOR(ITF_NUM_HID, 0, 0, sizeof(desc_hid_report), EPNUM_HID_OUT, EPNUM_HID_IN, 64, 0x08),
 };
 
 #if TUD_OPT_HIGH_SPEED
@@ -180,7 +177,7 @@ uint8_t const desc_hs_configuration[] =
   TUD_MSC_DESCRIPTOR(ITF_NUM_MSC, 5, EPNUM_MSC_OUT, EPNUM_MSC_IN, 512),
 
   // Interface number, string index, protocol, report descriptor len, EP In address, size & polling interval
-  //TUD_HID_DESCRIPTOR(ITF_NUM_HID, 6, HID_ITF_PROTOCOL_NONE, sizeof(desc_hid_report), EPNUM_HID, CFG_TUD_HID_EP_BUFSIZE, 5)
+  //TUD_HID_DESCRIPTOR(ITF_NUM_HID, 0, HID_ITF_PROTOCOL_NONE, sizeof(desc_hid_report), EPNUM_HID, CFG_TUD_HID_EP_BUFSIZE, 5)
   //TUD_HID_INOUT_DESCRIPTOR(ITF_NUM_HID, 0, HID_ITF_PROTOCOL_NONE, sizeof(desc_hid_report), EPNUM_HID, 0x80 | EPNUM_HID, CFG_TUD_HID_EP_BUFSIZE, 10)
 };
 #endif

From 00d99a427418a864dcb64e3ee727501dfbfaa6d5 Mon Sep 17 00:00:00 2001
From: HASUMI Hitoshi <hasumikin@gmail.com>
Date: Wed, 8 Sep 2021 12:58:28 +0900
Subject: [PATCH 03/10] =?UTF-8?q?MSC=20finally=20works=20=F0=9F=8E=89?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/msc_disk.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/src/msc_disk.c b/src/msc_disk.c
index 46c42537..63ff5b99 100644
--- a/src/msc_disk.c
+++ b/src/msc_disk.c
@@ -306,8 +306,10 @@ void msc_init(void)
 #endif
   {
     /* These functions know XIP_BASE so you just have to give them FLASH_TARGET_OFFSET */
+    uint32_t ints = save_and_disable_interrupts();
     flash_range_erase(FLASH_TARGET_OFFSET, SECTOR_SIZE * 4);
     flash_range_program(FLASH_TARGET_OFFSET, msc_disk[0], SECTOR_SIZE * 4);
+    restore_interrupts(ints);
   }
 }
 

From ab31af560fa31866dc8b1cc4426423cf1dfde59e Mon Sep 17 00:00:00 2001
From: HASUMI Hitoshi <hasumikin@gmail.com>
Date: Wed, 8 Sep 2021 15:05:17 +0900
Subject: [PATCH 04/10] =?UTF-8?q?=F0=9F=92=85?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 CMakeLists.txt                   |   1 -
 include/usb_descriptors.h        |  34 --------
 src/main.c                       |   4 +-
 src/usb.c                        | 143 -------------------------------
 src/usb_descriptors.c            | 123 ++++++++++++++++----------
 src/{usb.h => usb_descriptors.h} |  16 +++-
 src/version.h                    |   2 +-
 7 files changed, 95 insertions(+), 228 deletions(-)
 delete mode 100644 include/usb_descriptors.h
 delete mode 100644 src/usb.c
 rename src/{usb.h => usb_descriptors.h} (69%)

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 981e045b..8c968222 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -31,7 +31,6 @@ add_executable(${PROJECT}
   src/msc_disk.c
   src/usb_descriptors.c
   src/gpio.c
-  src/usb.c
   src/uart.c
   src/ws2812.c
   src/rotary_encoder.c
diff --git a/include/usb_descriptors.h b/include/usb_descriptors.h
deleted file mode 100644
index 1359d1f0..00000000
--- a/include/usb_descriptors.h
+++ /dev/null
@@ -1,34 +0,0 @@
-/* 
- * The MIT License (MIT)
- *
- * Copyright (c) 2019 Ha Thach (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 USB_DESCRIPTORS_H_
-#define USB_DESCRIPTORS_H_
-
-enum {
-    REPORT_ID_KEYBOARD = 1,
-    REPORT_ID_MOUSE
-};
-
-#endif /* USB_DESCRIPTORS_H_ */
-
diff --git a/src/main.c b/src/main.c
index 84c7f707..cb0d25f5 100644
--- a/src/main.c
+++ b/src/main.c
@@ -11,7 +11,7 @@
 /* mrbc_class */
 #include "msc_disk.h"
 #include "gpio.h"
-#include "usb.h"
+#include "usb_descriptors.h"
 #include "uart.h"
 #include "ws2812.h"
 #include "rotary_encoder.h"
@@ -80,7 +80,7 @@ int main() {
   mrbc_define_method(0, mrbc_class_object, "rand",         c_rand);
   MSC_INIT();
   GPIO_INIT();
-  USB_INIT();
+  TUD_INIT();
   UART_INIT();
   WS2812_INIT();
   ROTARY_ENCODER_INIT();
diff --git a/src/usb.c b/src/usb.c
deleted file mode 100644
index 160cdfc0..00000000
--- a/src/usb.c
+++ /dev/null
@@ -1,143 +0,0 @@
-#include "usb.h"
-
-#include "tusb.h"
-#include "usb_descriptors.h"
-#include "hardware/timer.h"
-#include "hardware/clocks.h"
-#include "hardware/irq.h"
-#include "hardware/structs/scb.h"
-#include "hardware/sync.h"
-#include "hardware/gpio.h"
-
-const uint8_t device_desc[] = {
-  18, // bLength
-  1, // bDescriptorType
-  0x10,
-  0x01, // bcdUSB
-  0x00, // bDeviceClass
-  0x00, // bDeviceSubClass
-  0x00, // bDeviceProtocol
-  CFG_TUD_ENDPOINT0_SIZE, // bMaxPacketSize0
-  0xfe,
-  0xca, // idVendor
-  0xcd,
-  0xab, // idProduct,
-  0x00,
-  0x00, // bcdDevice
-  0x00, // iManufacturer
-  0x01, // iProduct
-  0x00, // iSerialNumber
-  0x01, // bNumConfigurations
-};
-
-//const uint8_t conf_desc[] = {
-//  9, // bLength
-//  2, // bDescriptorType
-//  9 + 9,
-//  0, // wTotalLength
-//  1, // bNumInterface
-//  1, // bConfigurationValue
-//  0, // iConfiguration
-//  0x20, // bmAttributes
-//  0x0F, // bMaxPower
-//
-//  // --- Interface ---
-//  9, // bLength
-//  4, // bDescriptorType
-//  1, // bInterfaceNumber
-//  0, // bAlternateSetting
-//  0, // bNumEndpoints
-//  0xFF, // bInterfaceClass
-//  0xFF, // bInterfaceSubClass
-//  0xFF, // bInterfaceProtocol
-//  0, // iInterface
-//};
-
-const uint8_t hid_report_desc[] = {
-  TUD_HID_REPORT_DESC_KEYBOARD(HID_REPORT_ID(REPORT_ID_KEYBOARD)),
-  TUD_HID_REPORT_DESC_MOUSE(HID_REPORT_ID(REPORT_ID_MOUSE))
-};
-
-const uint8_t conf_desc[] = {
-    TUD_CONFIG_DESCRIPTOR(1, 1, 0, TUD_CONFIG_DESC_LEN + TUD_HID_INOUT_DESC_LEN, 0, 0x0F),
-    TUD_HID_INOUT_DESCRIPTOR(1, 0, 0, sizeof(hid_report_desc), 0x01, 0x81, 64, 0x0F)
-};
-
-const uint16_t string_desc_lang[] = { // Index: 0
-  4 | (3 << 8), // bLength & bDescriptorType
-  0
-};
-const uint16_t string_desc_product[] = { // Index: 1
-  16 | (3 << 8),
-  'P', 'R', 'K', 'f', 'i', 'r', 'm'
-};
-
-//uint8_t const *tud_descriptor_device_cb(void) {
-//  return device_desc;
-//}
-//uint8_t const *tud_descriptor_configuration_cb(uint8_t index) {
-//  return conf_desc;
-//}
-//uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid) {
-//  uint16_t const *ret = NULL;
-//  switch(index) {
-//    case 0:
-//      ret = string_desc_lang;
-//      break;
-//    case 1:
-//      ret = string_desc_product;
-//      break;
-//    default:
-//      break;
-//  }
-//  return ret;
-//}
-
-
-uint8_t const *tud_hid_descriptor_report_cb(uint8_t instance) {
-    return hid_report_desc;
-}
-uint16_t tud_hid_get_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t* buffer, uint16_t reqlen) {
-    return 0;
-}
-void tud_hid_set_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t const* buffer, uint16_t bufsize) {
-    return;
-}
-
-
-void
-c_tud_task(mrb_vm *vm, mrb_value *v, int argc)
-{
-  tud_task();
-}
-
-void
-c_report_hid(mrb_vm *vm, mrb_value *v, int argc)
-{
-  uint32_t const btn = 1;
-
-  // Remote wakeup
-  if (tud_suspended() && btn) {
-    // Wake up host if we are in suspend mode
-    // and REMOTE_WAKEUP feature is enabled by host
-    tud_remote_wakeup();
-  }
-
-  uint8_t modifier = GET_INT_ARG(1);
-  uint8_t *keycodes = GET_STRING_ARG(2);
-
-  /*------------- Keyboard -------------*/
-  if (tud_hid_ready()) {
-    tud_hid_keyboard_report(REPORT_ID_KEYBOARD, modifier, keycodes);
-  }
-}
-
-void
-c_tud_mounted_q(mrb_vm *vm, mrb_value *v, int argc)
-{
-  if (tud_mounted()) {
-    SET_TRUE_RETURN();
-  } else {
-    SET_FALSE_RETURN();
-  }
-}
diff --git a/src/usb_descriptors.c b/src/usb_descriptors.c
index 8c3c0436..b4abc848 100644
--- a/src/usb_descriptors.c
+++ b/src/usb_descriptors.c
@@ -25,6 +25,8 @@
 
 #include "tusb.h"
 
+#include "usb_descriptors.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.
  *
@@ -74,15 +76,6 @@ uint8_t const * tud_descriptor_device_cb(void)
 // Configuration Descriptor
 //--------------------------------------------------------------------+
 
-enum
-{
-  ITF_NUM_CDC = 0,
-  ITF_NUM_CDC_DATA,
-  ITF_NUM_MSC,
-  ITF_NUM_HID,
-  ITF_NUM_TOTAL
-};
-
 #define CONFIG_TOTAL_LEN    (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN + TUD_MSC_DESC_LEN + TUD_HID_INOUT_DESC_LEN)
 
 #if CFG_TUSB_MCU == OPT_MCU_LPC175X_6X || CFG_TUSB_MCU == OPT_MCU_LPC177X_8X || CFG_TUSB_MCU == OPT_MCU_LPC40XX
@@ -127,19 +120,6 @@ enum
 
 #endif
 
-//enum {
-//    REPORT_ID_KEYBOARD = 1,
-//    REPORT_ID_MOUSE
-//};
-enum
-{
-  REPORT_ID_KEYBOARD = 1,
-  REPORT_ID_MOUSE,
-  REPORT_ID_CONSUMER_CONTROL,
-  REPORT_ID_GAMEPAD,
-  REPORT_ID_COUNT
-};
-
 #define EPNUM_HID_IN    0x04
 #define EPNUM_HID_OUT   0x84
 
@@ -149,6 +129,14 @@ uint8_t const desc_hid_report[] =
   TUD_HID_REPORT_DESC_MOUSE   ( HID_REPORT_ID(REPORT_ID_MOUSE            )),
 };
 
+enum
+{
+  ITF_NUM_CDC = 0,
+  ITF_NUM_HID,
+  ITF_NUM_MSC,
+  ITF_NUM_TOTAL
+};
+
 uint8_t const desc_fs_configuration[] =
 {
   // Config number, interface count, string index, total length, attribute, power in mA
@@ -164,24 +152,6 @@ uint8_t const desc_fs_configuration[] =
   TUD_HID_INOUT_DESCRIPTOR(ITF_NUM_HID, 0, 0, sizeof(desc_hid_report), EPNUM_HID_OUT, EPNUM_HID_IN, 64, 0x08),
 };
 
-#if TUD_OPT_HIGH_SPEED
-uint8_t const desc_hs_configuration[] =
-{
-  // Config number, interface count, string index, total length, attribute, power in mA
-  TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100),
-
-  // Interface number, string index, EP notification address and size, EP data address (out, in) and size.
-  TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 4, EPNUM_CDC_NOTIF, 8, EPNUM_CDC_OUT, EPNUM_CDC_IN, 512),
-
-  // Interface number, string index, EP Out & EP In address, EP size
-  TUD_MSC_DESCRIPTOR(ITF_NUM_MSC, 5, EPNUM_MSC_OUT, EPNUM_MSC_IN, 512),
-
-  // Interface number, string index, protocol, report descriptor len, EP In address, size & polling interval
-  //TUD_HID_DESCRIPTOR(ITF_NUM_HID, 0, HID_ITF_PROTOCOL_NONE, sizeof(desc_hid_report), EPNUM_HID, CFG_TUD_HID_EP_BUFSIZE, 5)
-  //TUD_HID_INOUT_DESCRIPTOR(ITF_NUM_HID, 0, HID_ITF_PROTOCOL_NONE, sizeof(desc_hid_report), EPNUM_HID, 0x80 | EPNUM_HID, CFG_TUD_HID_EP_BUFSIZE, 10)
-};
-#endif
-
 
 // Invoked when received GET CONFIGURATION DESCRIPTOR
 // Application return pointer to descriptor
@@ -189,13 +159,7 @@ uint8_t const desc_hs_configuration[] =
 uint8_t const * tud_descriptor_configuration_cb(uint8_t index)
 {
   (void) index; // for multiple configurations
-
-#if TUD_OPT_HIGH_SPEED
-  // Although we are highspeed, host may be fullspeed.
-  return (tud_speed_get() == TUSB_SPEED_HIGH) ?  desc_hs_configuration : desc_fs_configuration;
-#else
   return desc_fs_configuration;
-#endif
 }
 
 //--------------------------------------------------------------------+
@@ -252,3 +216,70 @@ uint16_t const* tud_descriptor_string_cb(uint8_t index, uint16_t langid)
 
   return _desc_str;
 }
+
+
+//--------------------------------------------------------------------+
+// HID Descriptor
+//--------------------------------------------------------------------+
+
+const uint8_t hid_report_desc[] = {
+  TUD_HID_REPORT_DESC_KEYBOARD(HID_REPORT_ID(REPORT_ID_KEYBOARD)),
+  TUD_HID_REPORT_DESC_MOUSE(HID_REPORT_ID(REPORT_ID_MOUSE))
+};
+
+const uint16_t string_desc_product[] = { // Index: 1
+  16 | (3 << 8),
+  'P', 'R', 'K', 'f', 'i', 'r', 'm'
+};
+
+uint8_t const *tud_hid_descriptor_report_cb(uint8_t instance) {
+    return hid_report_desc;
+}
+uint16_t tud_hid_get_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t* buffer, uint16_t reqlen) {
+    return 0;
+}
+void tud_hid_set_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t const* buffer, uint16_t bufsize) {
+    return;
+}
+
+
+//--------------------------------------------------------------------+
+// Ruby methods
+//--------------------------------------------------------------------+
+
+void
+c_tud_task(mrb_vm *vm, mrb_value *v, int argc)
+{
+  tud_task();
+}
+
+void
+c_report_hid(mrb_vm *vm, mrb_value *v, int argc)
+{
+  uint32_t const btn = 1;
+
+  // Remote wakeup
+  if (tud_suspended() && btn) {
+    // Wake up host if we are in suspend mode
+    // and REMOTE_WAKEUP feature is enabled by host
+    tud_remote_wakeup();
+  }
+
+  uint8_t modifier = GET_INT_ARG(1);
+  uint8_t *keycodes = GET_STRING_ARG(2);
+
+  /*------------- Keyboard -------------*/
+  if (tud_hid_ready()) {
+    tud_hid_keyboard_report(REPORT_ID_KEYBOARD, modifier, keycodes);
+  }
+}
+
+void
+c_tud_mounted_q(mrb_vm *vm, mrb_value *v, int argc)
+{
+  if (tud_mounted()) {
+    SET_TRUE_RETURN();
+  } else {
+    SET_FALSE_RETURN();
+  }
+}
diff --git a/src/usb.h b/src/usb_descriptors.h
similarity index 69%
rename from src/usb.h
rename to src/usb_descriptors.h
index a992abf7..184baf2d 100644
--- a/src/usb.h
+++ b/src/usb_descriptors.h
@@ -1,14 +1,28 @@
 /* mruby/c VM */
 #include <mrubyc.h>
 
+#ifndef USB_DESCRIPTORS_H_
+#define USB_DESCRIPTORS_H_
+
+enum {
+  REPORT_ID_KEYBOARD = 1,
+  REPORT_ID_MOUSE,
+  REPORT_ID_CONSUMER_CONTROL,
+  REPORT_ID_GAMEPAD,
+  REPORT_ID_COUNT
+};
+
 void c_tud_task(mrb_vm *vm, mrb_value *v, int argc);
 void c_report_hid(mrb_vm *vm, mrb_value *v, int argc);
 void c_tud_mounted_q(mrb_vm *vm, mrb_value *v, int argc);
 
-#define USB_INIT() do { \
+#define TUD_INIT() do { \
   mrbc_define_method(0, mrbc_class_object,   "tud_task",     c_tud_task);      \
   mrbc_define_method(0, mrbc_class_object,   "tud_mounted?", c_tud_mounted_q); \
   mrbc_class *mrbc_class_Keyboard = mrbc_define_class(0, "Keyboard", mrbc_class_object); \
   mrbc_define_method(0, mrbc_class_Keyboard, "report_hid",   c_report_hid);    \
 } while (0)
 
+#endif /* USB_DESCRIPTORS_H_ */
+
+
diff --git a/src/version.h b/src/version.h
index 1f235afb..4652a730 100644
--- a/src/version.h
+++ b/src/version.h
@@ -1,5 +1,5 @@
 #pragma once
 
 #define PRK_VERSION   "0.9.0"
-#define PRK_BUILDDATE "20210907"
+#define PRK_BUILDDATE "20210908"
 #define PRK_REVISION  "e8ee115"

From 31da07781930e1f3a99cc404c829a47ed3cead32 Mon Sep 17 00:00:00 2001
From: HASUMI Hitoshi <hasumikin@gmail.com>
Date: Wed, 8 Sep 2021 16:59:49 +0900
Subject: [PATCH 05/10] wip

---
 src/main.c              | 55 +++++++++++++++++++++++++++++++++++++++++
 src/msc_disk.c          | 31 +----------------------
 src/msc_disk.h          | 35 ++++++++++++++++++++++++++
 src/ruby/lib/tud.rb     | 13 ++++++++++
 src/ruby/sig/object.rbs |  2 ++
 5 files changed, 106 insertions(+), 30 deletions(-)

diff --git a/src/main.c b/src/main.c
index cb0d25f5..b7bf1cf4 100644
--- a/src/main.c
+++ b/src/main.c
@@ -68,9 +68,60 @@ mrbc_load_model(const uint8_t *mrb)
   mrbc_raw_free(vm);
 }
 
+#ifndef NODE_BOX_SIZE
+#define NODE_BOX_SIZE 30
+#endif
+
+//mrbc_tcb*
+//autoreload(void)
+//{
+//  if (autoreload_state != AUTORELOAD_READY) return NULL;
+//  autoreload_state = AUTORELOAD_WAIT;
+//  DirEnt entry;
+//  msc_findDirEnt("KEYMAP  RB ", &entry);
+//  if (entry.Name[0] != '\0') {
+//    char *program = (char *)(FLASH_MMAP_ADDR + SECTOR_SIZE * (1 + entry.FstClusLO));
+//    ParserState *p = Compiler_parseInitState(NODE_BOX_SIZE);
+//    StreamInterface *si = StreamInterface_new(program, STREAM_TYPE_MEMORY);
+//    mrbc_tcb *tcb;
+//    if (Compiler_compile(p, si)) {
+//      tcb = mrbc_create_task(p->scope->vm_code, 0);
+//      p->scope->vm_code = NULL;
+//      Compiler_parserStateFree(p);
+//    }
+//    StreamInterface_free(si);
+//    return tcb;
+//  } else {
+//    return NULL;
+//  }
+//}
+
+
+
+mrbc_tcb *tcb_keymap;
+
 mrbc_tcb *tcb_rgb; /* from ws2812.h */
 
+//int autoreload_state; /* from msc_disk.h */
+
+//void
+//c_autoreload_bang(mrb_vm *vm, mrb_value *v, int argc)
+//{
+//}
+//
+//void
+//c_autoreload_ready_q(mrb_vm *vm, mrb_value *v, int argc)
+//{
+//  if (autoreload_state == AUTORELOAD_READY) {
+//    SET_TRUE_RETURN();
+//  } else {
+//    SET_FALSE_RETURN();
+//  }
+//}
+
 int main() {
+//  autoreload_state = AUTORELOAD_READY;
+
   stdio_init_all();
   board_init();
   tusb_init();
@@ -78,6 +129,8 @@ int main() {
   mrbc_init(memory_pool, MEMORY_SIZE);
   mrbc_define_method(0, mrbc_class_object, "board_millis", c_board_millis);
   mrbc_define_method(0, mrbc_class_object, "rand",         c_rand);
+//  mrbc_define_method(0, mrbc_class_object, "autoreload_ready?", c_autoreload_ready_q);
+//  mrbc_define_method(0, mrbc_class_object, "autoreload!",       c_autoreload_bang);
   MSC_INIT();
   GPIO_INIT();
   TUD_INIT();
@@ -93,6 +146,8 @@ int main() {
   mrbc_create_task(tud, 0);
   tcb_rgb = mrbc_create_task(rgb_task, 0);
   create_sandbox();
+  //tcb_keymap = autoreload();
+//autoreload_state = AUTORELOAD_WAIT;
   mrbc_create_task(keymap, 0);
   mrbc_run();
   return 0;
diff --git a/src/msc_disk.c b/src/msc_disk.c
index 63ff5b99..23ac5be9 100644
--- a/src/msc_disk.c
+++ b/src/msc_disk.c
@@ -33,11 +33,6 @@
 
 #include "msc_disk.h"
 
-#define FLASH_TARGET_OFFSET  0x00180000                       /* 1.5MB */
-#define FLASH_MMAP_ADDR      (XIP_BASE + FLASH_TARGET_OFFSET) /* 0x10180000 */
-#define SECTOR_SIZE          4096
-#define SECTOR_COUNT         128   /* 4096 * 128 = 512KB */
-
 // whether host does safe-eject
 static bool ejected = false;
 
@@ -313,25 +308,6 @@ void msc_init(void)
   }
 }
 
-/*
- * Directory entry in FAT Volume
- * (FAT data is little endian)
- */
-typedef struct dir_ent {
-  char     Name[11];
-  uint8_t  Attr;
-  uint8_t  NTRes;
-  uint8_t  CrtTimeTenth;
-  char     CrtTime[2];
-  char     CrtDate[2];
-  char     LastAccDate[2];
-  uint16_t FstClusHI;
-  char     WrtTime[2];
-  char     WrtDate[2];
-  uint16_t FstClusLO;
-  uint32_t FileSize;
-} DirEnt;
-
 /*
  * Only works in top directory
  * Only works if SFN (short file name)
@@ -359,12 +335,7 @@ void
 tud_msc_write10_complete_cb(uint8_t lun)
 {
   (void)lun;
-//  DirEnt entry;
-//  msc_findDirEnt("README  TXT", &entry);
-//  if (entry.Name[0] != '\0') {
-//    const char *program = (const char *)(FLASH_MMAP_ADDR + SECTOR_SIZE * (1 + entry.FstClusLO));
-//    ppp = program[0];
-//  }
+//  autoreload_state = AUTORELOAD_READY;
 }
 
 //--------------------------------------------------------------------+
diff --git a/src/msc_disk.h b/src/msc_disk.h
index 8e810e69..8048c58f 100644
--- a/src/msc_disk.h
+++ b/src/msc_disk.h
@@ -1,7 +1,42 @@
 /* mruby/c VM */
 #include <mrubyc.h>
 
+#define FLASH_TARGET_OFFSET  0x00180000                       /* 1.5MB */
+#define FLASH_MMAP_ADDR      (XIP_BASE + FLASH_TARGET_OFFSET) /* 0x10180000 */
+#define SECTOR_SIZE          4096
+#define SECTOR_COUNT         128   /* 4096 * 128 = 512KB */
+
+/*
+ * Directory entry in FAT Volume
+ * Note that FAT data is little endian
+ */
+typedef struct dir_ent {
+  char     Name[11];
+  uint8_t  Attr;
+  uint8_t  NTRes;
+  uint8_t  CrtTimeTenth;
+  char     CrtTime[2];
+  char     CrtDate[2];
+  char     LastAccDate[2];
+  uint16_t FstClusHI;
+  char     WrtTime[2];
+  char     WrtDate[2];
+  uint16_t FstClusLO;
+  uint32_t FileSize;
+} DirEnt;
+
+enum
+{
+  AUTORELOAD_WAIT = 0,
+  AUTORELOAD_READY
+};
+
+extern int autoreload_state;
+
 void msc_init(void);
+
+void msc_findDirEnt(const char *filename, DirEnt *entry);
+
 void c_cdc_task(mrb_vm *vm, mrb_value *v, int argc);
 
 #define MSC_INIT() do { \
diff --git a/src/ruby/lib/tud.rb b/src/ruby/lib/tud.rb
index 4c06522c..e7060392 100644
--- a/src/ruby/lib/tud.rb
+++ b/src/ruby/lib/tud.rb
@@ -4,10 +4,23 @@
 puts
 puts "Starting tud task ..."
 
+autoreload_tick = 0
+
 while true
   tud_task
   cdc_task
   if $encoders && !$encoders.empty?
     $encoders.each { |encoder| encoder.read }
   end
+#  if autoreload_ready?
+#    if autoreload_tick == 0
+#      puts "Autoreload is ready ..."
+#      autoreload_tick = 500
+#    elsif autoreload_tick == 1
+#      puts "Autoreload!"
+#      autoreload!
+#      autoreload_tick = 0
+#    end
+#    autoreload_tick -= 1
+#  end
 end
diff --git a/src/ruby/sig/object.rbs b/src/ruby/sig/object.rbs
index 6571a6dc..d2f3bcec 100644
--- a/src/ruby/sig/object.rbs
+++ b/src/ruby/sig/object.rbs
@@ -7,6 +7,8 @@ class Object
   def sandbox_result : () -> untyped
   def compile_ruby: (String) -> bool
   def invoke_ruby: () -> bool
+  def autoreload_ready?: () -> bool
+  def autoreload!: () -> void
 
   # Raspi
   def board_millis : -> Integer

From af4b820ae2e002e70fe85137218f7495e189e622 Mon Sep 17 00:00:00 2001
From: HASUMI Hitoshi <hasumikin@gmail.com>
Date: Wed, 8 Sep 2021 18:05:34 +0900
Subject: [PATCH 06/10] =?UTF-8?q?Fix=20bug=20=F0=9F=90=9B?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/usb_descriptors.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/usb_descriptors.c b/src/usb_descriptors.c
index b4abc848..b98a51b1 100644
--- a/src/usb_descriptors.c
+++ b/src/usb_descriptors.c
@@ -132,6 +132,7 @@ uint8_t const desc_hid_report[] =
 enum
 {
   ITF_NUM_CDC = 0,
+  ITF_NUM_CDC_DATA,
   ITF_NUM_HID,
   ITF_NUM_MSC,
   ITF_NUM_TOTAL

From 71e1fbd451095c41ca62067b6abb959160c41091 Mon Sep 17 00:00:00 2001
From: HASUMI Hitoshi <hasumikin@gmail.com>
Date: Wed, 8 Sep 2021 18:05:54 +0900
Subject: [PATCH 07/10] autoreload on (only) startup!!!!!!!

---
 CMakeLists.txt |  9 +++++++-
 src/main.c     | 58 +++++++++++++++++++++++++++-----------------------
 2 files changed, 39 insertions(+), 28 deletions(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 8c968222..42c9762b 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -6,7 +6,14 @@ include(pico_sdk_import.cmake)
 
 project(prk_firmware)
 
-add_definitions(-DMRBC_USE_HAL_RP2040 -DMRBC_REQUIRE_32BIT_ALIGNMENT -DMAX_REGS_SIZE=256 -DMAX_SYMBOLS_COUNT=500 -DMAX_VM_COUNT=10)
+add_definitions(
+  -DNDEBUG
+  -DMRBC_USE_HAL_RP2040
+  -DMRBC_REQUIRE_32BIT_ALIGNMENT
+  -DMAX_REGS_SIZE=256
+  -DMAX_SYMBOLS_COUNT=500
+  -DMAX_VM_COUNT=10
+)
 
 # initialize the Raspberry Pi Pico SDK
 pico_sdk_init()
diff --git a/src/main.c b/src/main.c
index b7bf1cf4..68dd93a7 100644
--- a/src/main.c
+++ b/src/main.c
@@ -72,29 +72,29 @@ mrbc_load_model(const uint8_t *mrb)
 #define NODE_BOX_SIZE 30
 #endif
 
-//mrbc_tcb*
-//autoreload(void)
-//{
-//  if (autoreload_state != AUTORELOAD_READY) return NULL;
-//  autoreload_state = AUTORELOAD_WAIT;
-//  DirEnt entry;
-//  msc_findDirEnt("KEYMAP  RB ", &entry);
-//  if (entry.Name[0] != '\0') {
-//    char *program = (char *)(FLASH_MMAP_ADDR + SECTOR_SIZE * (1 + entry.FstClusLO));
-//    ParserState *p = Compiler_parseInitState(NODE_BOX_SIZE);
-//    StreamInterface *si = StreamInterface_new(program, STREAM_TYPE_MEMORY);
-//    mrbc_tcb *tcb;
-//    if (Compiler_compile(p, si)) {
-//      tcb = mrbc_create_task(p->scope->vm_code, 0);
-//      p->scope->vm_code = NULL;
-//      Compiler_parserStateFree(p);
-//    }
-//    StreamInterface_free(si);
-//    return tcb;
-//  } else {
-//    return NULL;
-//  }
-//}
+mrbc_tcb*
+autoreload(void)
+{
+  if (autoreload_state != AUTORELOAD_READY) return NULL;
+  autoreload_state = AUTORELOAD_WAIT;
+  DirEnt entry;
+  msc_findDirEnt("KEYMAP  RB ", &entry);
+  if (entry.Name[0] != '\0') {
+    char *program = (char *)(FLASH_MMAP_ADDR + SECTOR_SIZE * (1 + entry.FstClusLO));
+    ParserState *p = Compiler_parseInitState(NODE_BOX_SIZE);
+    StreamInterface *si = StreamInterface_new(program, STREAM_TYPE_MEMORY);
+    mrbc_tcb *tcb;
+    if (Compiler_compile(p, si)) {
+      tcb = mrbc_create_task(p->scope->vm_code, 0);
+      p->scope->vm_code = NULL;
+      Compiler_parserStateFree(p);
+    }
+    StreamInterface_free(si);
+    return tcb;
+  } else {
+    return NULL;
+  }
+}
 
 
 
@@ -102,7 +102,7 @@ mrbc_tcb *tcb_keymap;
 
 mrbc_tcb *tcb_rgb; /* from ws2812.h */
 
-//int autoreload_state; /* from msc_disk.h */
+int autoreload_state; /* from msc_disk.h */
 
 //void
 //c_autoreload_bang(mrb_vm *vm, mrb_value *v, int argc)
@@ -119,8 +119,12 @@ mrbc_tcb *tcb_rgb; /* from ws2812.h */
 //  }
 //}
 
+int loglevel;
+
 int main() {
-//  autoreload_state = AUTORELOAD_READY;
+  loglevel = LOGLEVEL_WARN;
+
+  autoreload_state = AUTORELOAD_READY;
 
   stdio_init_all();
   board_init();
@@ -146,9 +150,9 @@ int main() {
   mrbc_create_task(tud, 0);
   tcb_rgb = mrbc_create_task(rgb_task, 0);
   create_sandbox();
-  //tcb_keymap = autoreload();
+  tcb_keymap = autoreload();
 //autoreload_state = AUTORELOAD_WAIT;
-  mrbc_create_task(keymap, 0);
+//  mrbc_create_task(keymap, 0);
   mrbc_run();
   return 0;
 }

From 6e77670b1b8677f16bf27a1b8d7f9682822381a0 Mon Sep 17 00:00:00 2001
From: HASUMI Hitoshi <hasumikin@gmail.com>
Date: Thu, 9 Sep 2021 19:52:14 +0900
Subject: [PATCH 08/10] Autoreload works!!!!!

---
 CHANGELOG.md                   |   6 +++
 README.md                      |  39 ++++++++++++--
 doc/images/drag_and_drop_1.png | Bin 0 -> 27126 bytes
 doc/images/drag_and_drop_2.png | Bin 0 -> 28235 bytes
 src/main.c                     |  94 +++++++++++++++++++++------------
 src/msc_disk.c                 |   2 +-
 src/ruby/lib/tud.rb            |  25 +++++----
 src/ruby/sig/object.rbs        |   3 +-
 8 files changed, 116 insertions(+), 53 deletions(-)
 create mode 100644 doc/images/drag_and_drop_1.png
 create mode 100644 doc/images/drag_and_drop_2.png

diff --git a/CHANGELOG.md b/CHANGELOG.md
index e18a3385..c0573358 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,11 @@
 # Change log
 
+## 2021/09/10
+### BIG BIG IMPROVEMENT 🍣
+- You no longer need any compiler toolchain!!!
+- No longer detaching USB cable every time amending your keymap, too!!!
+- See README.md 👀
+
 ## 2021/08/17
 ### Breaking Change 💣
 - Code upgraded to correspond to the newest pico-sdk as of today. Please upgrade your pico-sdk
diff --git a/README.md b/README.md
index a5629443..3d0d0095 100644
--- a/README.md
+++ b/README.md
@@ -38,6 +38,32 @@ _(left: Raspberry Pi Pico / right: Sparkfun Pro Micro RP2040)_
 
 ### Getting started
 
+There are two ways to install PRK Fiwmware:
+
+1. [Using a release binary (recommended)](#Using-a-release-binary)
+2. [Building a binary by yourself](Building-a-binary-by-yourself)
+
+Anyhow, you should:
+
+- Be knowledgeable how to install a UF2 file into Raspi Pico on [https://www.raspberrypi.org/documentation/rp2040/getting-started/#getting-started-with-c](https://www.raspberrypi.org/documentation/rp2040/getting-started/#getting-started-with-c)
+  - [https://learn.sparkfun.com/tutorials/pro-micro-rp2040-hookup-guide](https://learn.sparkfun.com/tutorials/pro-micro-rp2040-hookup-guide) will also help you if you use Sparkfun Pro Micro RP2040
+
+#### Using a release binary
+
+- Download the newest release binary from [Releases](https://github.com/picoruby/prk_firmware/releases)
+
+- Unzip it. You should get a file that looks like `prk_firmware-0.9.0-20210910-xxxxxxxx.uf2`
+
+- Flash the uf2 into RP2040
+
+  ![](doc/images/drag_and_drop_1.png)
+
+- 
+
+#### Building a binary by yourself
+
+You may not want PRK Firmware to be a mass storage device in case that your employer doesn't allow you to bring a USB memory 🙈
+
 - Install CRuby (MRI) because "Static type checking" by [Steep](https://github.com/soutaro/steep) will be invoked in build process
 
 - Setup Raspberry Pi Pico C/C++ SDK
@@ -45,9 +71,6 @@ _(left: Raspberry Pi Pico / right: Sparkfun Pro Micro RP2040)_
   - Follow the instructions on [https://github.com/raspberrypi/pico-sdk#quick-start-your-own-project](https://github.com/raspberrypi/pico-sdk#quick-start-your-own-project)
     - Make sure you have `PICO_SDK_PATH` environment variable
 
-  - Be knowledgeable how to install a UF2 file into Raspi Pico on [https://www.raspberrypi.org/documentation/rp2040/getting-started/#getting-started-with-c](https://www.raspberrypi.org/documentation/rp2040/getting-started/#getting-started-with-c)
-
-  - [https://learn.sparkfun.com/tutorials/pro-micro-rp2040-hookup-guide](https://learn.sparkfun.com/tutorials/pro-micro-rp2040-hookup-guide) will also help you if you use Sparkfun Pro Micro RP2040
 
 - Clone the `prk_firmware` (this repository) wherever you like
 
@@ -63,15 +86,21 @@ _(left: Raspberry Pi Pico / right: Sparkfun Pro Micro RP2040)_
     git clone https://github.com/picoruby/prk_meishi2.git
     ```
 
+- Edit `prk_meishi2/keymap.rb` as you wish
+
 - Build with `cmake` and `make`
 
     ```
     cd prk_meishi2/build
-    cmake ../../..
+    cmake -DPRK_NO_MSC=1 ../../..
     make
     ```
+    
+    (Defining PRK_NO_MSC macro will avoid implementing mass storage feature)
+
+    Now you should have `prk_firmware-[version]-[date]-[revision].uf2` file in `prk_firmware/keyboards/prk_meishi2/build/` directory which includes your keymap in code.
 
-    Now you should have `prk_firmware.uf2` file in `prk_firmware/keyboards/prk_meishi2/build/` directory.
+- Install .uf2 into RP2040
 
 ### Contributing
 
diff --git a/doc/images/drag_and_drop_1.png b/doc/images/drag_and_drop_1.png
new file mode 100644
index 0000000000000000000000000000000000000000..a8af438b92d9ca130998ded23728be9e90b0103e
GIT binary patch
literal 27126
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
X#|S6uPb{vZI|$y%tI8G0eDwcsGIlyp

literal 0
HcmV?d00001

diff --git a/doc/images/drag_and_drop_2.png b/doc/images/drag_and_drop_2.png
new file mode 100644
index 0000000000000000000000000000000000000000..07572fee91b7e0bf78ce88d1581e3cb60b4c823f
GIT binary patch
literal 28235
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%0D|?j(rrKR7JgismUN&>o_8cYbtE
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

literal 0
HcmV?d00001

diff --git a/src/main.c b/src/main.c
index 68dd93a7..99d883b8 100644
--- a/src/main.c
+++ b/src/main.c
@@ -69,31 +69,49 @@ mrbc_load_model(const uint8_t *mrb)
 }
 
 #ifndef NODE_BOX_SIZE
-#define NODE_BOX_SIZE 30
+#define NODE_BOX_SIZE 50
 #endif
 
-mrbc_tcb*
-autoreload(void)
+mrbc_tcb *
+create_keymap_task(mrbc_tcb *tcb)
 {
-  if (autoreload_state != AUTORELOAD_READY) return NULL;
-  autoreload_state = AUTORELOAD_WAIT;
+  hal_disable_irq();
   DirEnt entry;
+  char *program;
+  StreamInterface *si;
+  ParserState *p = Compiler_parseInitState(NODE_BOX_SIZE);
   msc_findDirEnt("KEYMAP  RB ", &entry);
   if (entry.Name[0] != '\0') {
-    char *program = (char *)(FLASH_MMAP_ADDR + SECTOR_SIZE * (1 + entry.FstClusLO));
-    ParserState *p = Compiler_parseInitState(NODE_BOX_SIZE);
-    StreamInterface *si = StreamInterface_new(program, STREAM_TYPE_MEMORY);
-    mrbc_tcb *tcb;
-    if (Compiler_compile(p, si)) {
-      tcb = mrbc_create_task(p->scope->vm_code, 0);
-      p->scope->vm_code = NULL;
-      Compiler_parserStateFree(p);
-    }
+    program = (char *)(FLASH_MMAP_ADDR + SECTOR_SIZE * (1 + entry.FstClusLO));
+    si = StreamInterface_new(program, STREAM_TYPE_MEMORY);
+  } else {
+    si = StreamInterface_new("suspend_task", STREAM_TYPE_MEMORY);
+  }
+  if (!Compiler_compile(p, si)) {
+    Compiler_parserStateFree(p);
     StreamInterface_free(si);
-    return tcb;
+    p = Compiler_parseInitState(NODE_BOX_SIZE);
+    si = StreamInterface_new("suspend_task", STREAM_TYPE_MEMORY);
+    Compiler_compile(p, si);
+  }
+  if (tcb == NULL) {
+    tcb = mrbc_create_task(p->scope->vm_code, 0);
   } else {
-    return NULL;
+    mrbc_vm *vm = (mrbc_vm *)(&tcb->vm);
+    int vm_id = vm->vm_id;
+    mrbc_vm_end(vm);
+    memset(vm, 0, sizeof(mrbc_vm));
+    mrbc_load_mrb(vm, p->scope->vm_code);
+    vm->vm_id = vm_id;
+    mrbc_vm_begin(vm);
   }
+  p->scope->vm_code = NULL;
+  Compiler_parserStateFree(p);
+  StreamInterface_free(si);
+  autoreload_state = AUTORELOAD_WAIT;
+  hal_enable_irq();
+  mrbc_resume_task(tcb);
+  return tcb;
 }
 
 
@@ -104,20 +122,27 @@ mrbc_tcb *tcb_rgb; /* from ws2812.h */
 
 int autoreload_state; /* from msc_disk.h */
 
-//void
-//c_autoreload_bang(mrb_vm *vm, mrb_value *v, int argc)
-//{
-//}
-//
-//void
-//c_autoreload_ready_q(mrb_vm *vm, mrb_value *v, int argc)
-//{
-//  if (autoreload_state == AUTORELOAD_READY) {
-//    SET_TRUE_RETURN();
-//  } else {
-//    SET_FALSE_RETURN();
-//  }
-//}
+void
+c_suspend_keymap(mrb_vm *vm, mrb_value *v, int argc)
+{
+  mrbc_suspend_task(tcb_keymap);
+}
+
+void
+c_reload_keymap(mrb_vm *vm, mrb_value *v, int argc)
+{
+  tcb_keymap = create_keymap_task(tcb_keymap);
+}
+
+void
+c_autoreload_ready_q(mrb_vm *vm, mrb_value *v, int argc)
+{
+  if (autoreload_state == AUTORELOAD_READY) {
+    SET_TRUE_RETURN();
+  } else {
+    SET_FALSE_RETURN();
+  }
+}
 
 int loglevel;
 
@@ -133,8 +158,9 @@ int main() {
   mrbc_init(memory_pool, MEMORY_SIZE);
   mrbc_define_method(0, mrbc_class_object, "board_millis", c_board_millis);
   mrbc_define_method(0, mrbc_class_object, "rand",         c_rand);
-//  mrbc_define_method(0, mrbc_class_object, "autoreload_ready?", c_autoreload_ready_q);
-//  mrbc_define_method(0, mrbc_class_object, "autoreload!",       c_autoreload_bang);
+  mrbc_define_method(0, mrbc_class_object, "autoreload_ready?", c_autoreload_ready_q);
+  mrbc_define_method(0, mrbc_class_object, "reload_keymap",     c_reload_keymap);
+  mrbc_define_method(0, mrbc_class_object, "suspend_keymap",    c_suspend_keymap);
   MSC_INIT();
   GPIO_INIT();
   TUD_INIT();
@@ -150,9 +176,7 @@ int main() {
   mrbc_create_task(tud, 0);
   tcb_rgb = mrbc_create_task(rgb_task, 0);
   create_sandbox();
-  tcb_keymap = autoreload();
-//autoreload_state = AUTORELOAD_WAIT;
-//  mrbc_create_task(keymap, 0);
+  tcb_keymap = create_keymap_task(NULL);
   mrbc_run();
   return 0;
 }
diff --git a/src/msc_disk.c b/src/msc_disk.c
index 23ac5be9..a47630a7 100644
--- a/src/msc_disk.c
+++ b/src/msc_disk.c
@@ -335,7 +335,7 @@ void
 tud_msc_write10_complete_cb(uint8_t lun)
 {
   (void)lun;
-//  autoreload_state = AUTORELOAD_READY;
+  autoreload_state = AUTORELOAD_READY;
 }
 
 //--------------------------------------------------------------------+
diff --git a/src/ruby/lib/tud.rb b/src/ruby/lib/tud.rb
index e7060392..320d2c48 100644
--- a/src/ruby/lib/tud.rb
+++ b/src/ruby/lib/tud.rb
@@ -12,15 +12,18 @@
   if $encoders && !$encoders.empty?
     $encoders.each { |encoder| encoder.read }
   end
-#  if autoreload_ready?
-#    if autoreload_tick == 0
-#      puts "Autoreload is ready ..."
-#      autoreload_tick = 500
-#    elsif autoreload_tick == 1
-#      puts "Autoreload!"
-#      autoreload!
-#      autoreload_tick = 0
-#    end
-#    autoreload_tick -= 1
-#  end
+  if autoreload_ready?
+    if autoreload_tick == 0
+      puts "Autoreload is ready ..."
+      autoreload_tick = 500
+    elsif autoreload_tick == 1
+      puts "Suspending keymap ..."
+      suspend_keymap
+      sleep_ms 100
+      puts "Reloading keymap ..."
+      reload_keymap
+      puts "Reloaded keymap"
+    end
+    autoreload_tick -= 1
+  end
 end
diff --git a/src/ruby/sig/object.rbs b/src/ruby/sig/object.rbs
index d2f3bcec..0e10d4d3 100644
--- a/src/ruby/sig/object.rbs
+++ b/src/ruby/sig/object.rbs
@@ -8,7 +8,8 @@ class Object
   def compile_ruby: (String) -> bool
   def invoke_ruby: () -> bool
   def autoreload_ready?: () -> bool
-  def autoreload!: () -> void
+  def suspend_keymap: () -> void
+  def reload_keymap: () -> void
 
   # Raspi
   def board_millis : -> Integer

From 5a6897661191307224c3f5a193d8d246309e673f Mon Sep 17 00:00:00 2001
From: HASUMI Hitoshi <hasumikin@gmail.com>
Date: Thu, 9 Sep 2021 23:12:43 +0900
Subject: [PATCH 09/10] wip

---
 CMakeLists.txt        | 34 +++++++++++++++-----
 README.md             | 28 +++++++++++++----
 build/.gitignore      |  3 ++
 build/.keep           |  0
 src/main.c            | 73 +++++++++++++++++++++++++------------------
 src/msc_disk.c        | 13 ++++----
 src/msc_disk.h        |  3 +-
 src/usb_descriptors.c |  8 +++++
 src/version.h         |  4 +--
 9 files changed, 111 insertions(+), 55 deletions(-)
 create mode 100644 build/.gitignore
 create mode 100644 build/.keep

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 42c9762b..bd8f7d82 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -14,6 +14,9 @@ add_definitions(
   -DMAX_SYMBOLS_COUNT=500
   -DMAX_VM_COUNT=10
 )
+if(PRK_NO_MSC)
+  add_definitions(-DPRK_NO_MSC)
+endif()
 
 # initialize the Raspberry Pi Pico SDK
 pico_sdk_init()
@@ -28,8 +31,11 @@ set (PRK_BUILDDATE ${CMAKE_BUILDDATE})
 set (PRK_REVISION  ${CMAKE_REVISION})
 configure_file ("${CMAKE_CURRENT_SOURCE_DIR}/src/version.h.in" "${CMAKE_CURRENT_SOURCE_DIR}/src/version.h")
 
-set(PROJECT "prk_firmware-${PRK_VERSION}-${PRK_BUILDDATE}-${PRK_REVISION}")
-#set(PROJECT "prk_firmware")  # for test
+if(PRK_NO_MSC)
+  set(PROJECT "prk_firmware-${PRK_VERSION}-${PRK_BUILDDATE}-no_msc")
+else()
+  set(PROJECT "prk_firmware-${PRK_VERSION}-${PRK_BUILDDATE}-${PRK_REVISION}")
+endif()
 
 add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/lib)
 
@@ -48,7 +54,7 @@ pico_generate_pio_header(${PROJECT} ${CMAKE_CURRENT_LIST_DIR}/src/uart_tx.pio)
 pico_generate_pio_header(${PROJECT} ${CMAKE_CURRENT_LIST_DIR}/src/ws2812.pio)
 
 set(RBC ${CMAKE_CURRENT_SOURCE_DIR}/lib/picoruby/build/bin/host-production/alloc_libc/picorbc)
-#set(RBC RBENV_VERSION=mruby-3.0.0 mrbc)
+#set(RBC RBENV_VERSION=mruby-3.0.0 mrbc) # In case of PicoRuby compiler enbugged 😵
 
 add_custom_target(steep
   COMMAND bundle exec steep -h || bundle install
@@ -56,6 +62,16 @@ add_custom_target(steep
   WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/src/ruby
 )
 
+if(PRK_NO_MSC)
+  add_custom_target(keymap
+    COMMAND ${RBC} -Bkeymap         ${PROJECT_BINARY_DIR}/../keymap.rb
+    COMMAND mv ${PROJECT_BINARY_DIR}/../keymap.c ./
+    WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/src/ruby/lib
+  )
+else()
+  add_custom_target(keymap)
+endif()
+
 add_custom_target(ruby
   COMMAND ${RBC} -Bcore           core.rb
   COMMAND ${RBC} -Bkeyboard       keyboard.rb
@@ -63,12 +79,10 @@ add_custom_target(ruby
   COMMAND ${RBC} -Brotary_encoder rotary_encoder.rb
   COMMAND ${RBC} -Btud            tud.rb
   COMMAND ${RBC} -Brgb_task       rgb_task.rb
-  COMMAND ${RBC} -Bkeymap         ${PROJECT_BINARY_DIR}/../keymap.rb
-  COMMAND mv ${PROJECT_BINARY_DIR}/../keymap.c ./
   WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/src/ruby/lib
 )
 
-add_custom_target(ruby_2
+add_custom_target(sandbox
   COMMAND ${RBC} -Bbuffer         buffer.rb
   COMMAND ${RBC} -Bsandbox        sandbox.rb
   WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/lib/picoruby/cli/ruby
@@ -77,12 +91,16 @@ add_custom_target(ruby_2
 add_dependencies(ruby
   steep
 )
-
 add_dependencies(${PROJECT}
   picoruby
   ruby
-  ruby_2
+  sandbox
 )
+if(PRK_NO_MSC)
+  add_dependencies(${PROJECT}
+    keymap
+  )
+endif()
 
 target_link_libraries(${PROJECT}
   pico_stdlib
diff --git a/README.md b/README.md
index 3d0d0095..9ba26897 100644
--- a/README.md
+++ b/README.md
@@ -29,7 +29,7 @@ _(left: Raspberry Pi Pico / right: Sparkfun Pro Micro RP2040)_
   - [ ] Asymmetrical type. eg) ???
   - [x] UART communication between left and right
   - [ ] I2C communication between left and right
-- [ ] Macros
+- [x] Macros
 - [ ] Media keys
 - [x] RGBLED. An example on [picoruby/prk_crkbd](https://github.com/picoruby/prk_crkbd/blob/main/keymap.rb#L61-L76)
 - [ ] OLED display
@@ -46,7 +46,7 @@ There are two ways to install PRK Fiwmware:
 Anyhow, you should:
 
 - Be knowledgeable how to install a UF2 file into Raspi Pico on [https://www.raspberrypi.org/documentation/rp2040/getting-started/#getting-started-with-c](https://www.raspberrypi.org/documentation/rp2040/getting-started/#getting-started-with-c)
-  - [https://learn.sparkfun.com/tutorials/pro-micro-rp2040-hookup-guide](https://learn.sparkfun.com/tutorials/pro-micro-rp2040-hookup-guide) will also help you if you use Sparkfun Pro Micro RP2040
+  - [https://learn.sparkfun.com/tutorials/pro-micro-rp2040-hookup-guide](https://learn.sparkfun.com/tutorials/pro-micro-rp2040-hookup-guide) will also be helpful if you use Sparkfun Pro Micro RP2040
 
 #### Using a release binary
 
@@ -58,7 +58,11 @@ Anyhow, you should:
 
   ![](doc/images/drag_and_drop_1.png)
 
-- 
+- `PRKFirmware` mass storage drive should be mounted, then drag and drop your `keymap.rb`
+
+  ![](doc/images/drag_and_drop_2.png)
+
+Your keyboard will automatically reboot. Enjoy!
 
 #### Building a binary by yourself
 
@@ -98,13 +102,25 @@ You may not want PRK Firmware to be a mass storage device in case that your empl
     
     (Defining PRK_NO_MSC macro will avoid implementing mass storage feature)
 
-    Now you should have `prk_firmware-[version]-[date]-[revision].uf2` file in `prk_firmware/keyboards/prk_meishi2/build/` directory which includes your keymap in code.
+    Now you should have `prk_firmware-[version]-[date]-no_msc.uf2` file in `prk_firmware/keyboards/prk_meishi2/build/` directory which includes your keymap in code.
+
+- Install that `.uf2` file into RP2040
 
-- Install .uf2 into RP2040
+### What if split type keyboard?
+
+- Make sure installing your setup on both side
 
 ### Contributing
 
-Fork, clone, patch and send a pull request.
+#### Building uf2 of excluding-keymap-version
+
+```
+cd prk_firmware/build
+cmake -DCMAKE_BUILD_TYPE=Debug ..
+make
+```
+
+Then patch and send a pull request.
 
 #### For those who are willing to contribute to PRK or write your own keymaps:
 
diff --git a/build/.gitignore b/build/.gitignore
new file mode 100644
index 00000000..edd99d5c
--- /dev/null
+++ b/build/.gitignore
@@ -0,0 +1,3 @@
+*
+!.keep
+!.gitignore
diff --git a/build/.keep b/build/.keep
new file mode 100644
index 00000000..e69de29b
diff --git a/src/main.c b/src/main.c
index 99d883b8..7ea4ed8b 100644
--- a/src/main.c
+++ b/src/main.c
@@ -27,7 +27,9 @@
 /* tasks */
 #include "ruby/lib/tud.c"
 #include "ruby/lib/rgb_task.c"
+#ifdef PRK_NO_MSC
 #include "ruby/lib/keymap.c"
+#endif
 
 void
 c_board_millis(mrb_vm *vm, mrb_value *v, int argc)
@@ -47,26 +49,9 @@ c_rand(mrb_vm *vm, mrb_value *v, int argc)
   SET_INT_RETURN(rand());
 }
 
-#define MEMORY_SIZE (1024*200)
-
-static uint8_t memory_pool[MEMORY_SIZE];
+int autoreload_state; /* from msc_disk.h */
 
-void
-mrbc_load_model(const uint8_t *mrb)
-{
-  mrbc_vm *vm = mrbc_vm_open(NULL);
-  if( vm == 0 ) {
-    console_printf("Error: Can't open VM.\n");
-    return;
-  }
-  if( mrbc_load_mrb(vm, mrb) != 0 ) {
-    console_printf("Error: Illegal bytecode.\n");
-    return;
-  }
-  mrbc_vm_begin(vm);
-  mrbc_vm_run(vm);
-  mrbc_raw_free(vm);
-}
+#ifndef PRK_NO_MSC
 
 #ifndef NODE_BOX_SIZE
 #define NODE_BOX_SIZE 50
@@ -114,14 +99,8 @@ create_keymap_task(mrbc_tcb *tcb)
   return tcb;
 }
 
-
-
 mrbc_tcb *tcb_keymap;
 
-mrbc_tcb *tcb_rgb; /* from ws2812.h */
-
-int autoreload_state; /* from msc_disk.h */
-
 void
 c_suspend_keymap(mrb_vm *vm, mrb_value *v, int argc)
 {
@@ -144,24 +123,48 @@ c_autoreload_ready_q(mrb_vm *vm, mrb_value *v, int argc)
   }
 }
 
+#endif /* PRK_NO_MSC */
+
+
+#define MEMORY_SIZE (1024*200)
+
+static uint8_t memory_pool[MEMORY_SIZE];
+
+void
+mrbc_load_model(const uint8_t *mrb)
+{
+  mrbc_vm *vm = mrbc_vm_open(NULL);
+  if( vm == 0 ) {
+    console_printf("Error: Can't open VM.\n");
+    return;
+  }
+  if( mrbc_load_mrb(vm, mrb) != 0 ) {
+    console_printf("Error: Illegal bytecode.\n");
+    return;
+  }
+  mrbc_vm_begin(vm);
+  mrbc_vm_run(vm);
+  mrbc_raw_free(vm);
+}
+
+mrbc_tcb *tcb_rgb; /* from ws2812.h */
+
 int loglevel;
 
 int main() {
   loglevel = LOGLEVEL_WARN;
 
-  autoreload_state = AUTORELOAD_READY;
 
   stdio_init_all();
   board_init();
   tusb_init();
-  msc_init();
   mrbc_init(memory_pool, MEMORY_SIZE);
   mrbc_define_method(0, mrbc_class_object, "board_millis", c_board_millis);
   mrbc_define_method(0, mrbc_class_object, "rand",         c_rand);
-  mrbc_define_method(0, mrbc_class_object, "autoreload_ready?", c_autoreload_ready_q);
-  mrbc_define_method(0, mrbc_class_object, "reload_keymap",     c_reload_keymap);
-  mrbc_define_method(0, mrbc_class_object, "suspend_keymap",    c_suspend_keymap);
-  MSC_INIT();
+#ifndef PRK_NO_MSC
+  msc_init();
+#endif
+  CDC_INIT();
   GPIO_INIT();
   TUD_INIT();
   UART_INIT();
@@ -176,7 +179,15 @@ int main() {
   mrbc_create_task(tud, 0);
   tcb_rgb = mrbc_create_task(rgb_task, 0);
   create_sandbox();
+#ifdef PRK_NO_MSC
+  mrbc_create_task(keymap, 0);
+#else
+  mrbc_define_method(0, mrbc_class_object, "autoreload_ready?", c_autoreload_ready_q);
+  mrbc_define_method(0, mrbc_class_object, "reload_keymap",     c_reload_keymap);
+  mrbc_define_method(0, mrbc_class_object, "suspend_keymap",    c_suspend_keymap);
+  autoreload_state = AUTORELOAD_READY;
   tcb_keymap = create_keymap_task(NULL);
+#endif
   mrbc_run();
   return 0;
 }
diff --git a/src/msc_disk.c b/src/msc_disk.c
index a47630a7..b381e408 100644
--- a/src/msc_disk.c
+++ b/src/msc_disk.c
@@ -23,15 +23,15 @@
  *
  */
 
-#include "bsp/board.h"
-#include "tusb.h"
-
-#if CFG_TUD_MSC
-
 #include "hardware/flash.h"
 #include "hardware/sync.h"
+#include "bsp/board.h"
+
+#include "tusb.h"
 
 #include "msc_disk.h"
+#include <mrubyc.h>
+
 
 // whether host does safe-eject
 static bool ejected = false;
@@ -338,6 +338,7 @@ tud_msc_write10_complete_cb(uint8_t lun)
   autoreload_state = AUTORELOAD_READY;
 }
 
+
 //--------------------------------------------------------------------+
 // USB CDC
 //--------------------------------------------------------------------+
@@ -387,5 +388,3 @@ void tud_cdc_rx_cb(uint8_t itf)
 {
   (void) itf;
 }
-
-#endif
diff --git a/src/msc_disk.h b/src/msc_disk.h
index 8048c58f..7b7db483 100644
--- a/src/msc_disk.h
+++ b/src/msc_disk.h
@@ -39,6 +39,7 @@ void msc_findDirEnt(const char *filename, DirEnt *entry);
 
 void c_cdc_task(mrb_vm *vm, mrb_value *v, int argc);
 
-#define MSC_INIT() do { \
+#define CDC_INIT() do { \
   mrbc_define_method(0, mrbc_class_object, "cdc_task",  c_cdc_task);  \
 } while (0)
+
diff --git a/src/usb_descriptors.c b/src/usb_descriptors.c
index b98a51b1..19d4cbcc 100644
--- a/src/usb_descriptors.c
+++ b/src/usb_descriptors.c
@@ -76,7 +76,11 @@ uint8_t const * tud_descriptor_device_cb(void)
 // Configuration Descriptor
 //--------------------------------------------------------------------+
 
+#ifdef PRK_NO_MSC
+#define CONFIG_TOTAL_LEN    (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN + TUD_HID_INOUT_DESC_LEN)
+#else
 #define CONFIG_TOTAL_LEN    (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN + TUD_MSC_DESC_LEN + TUD_HID_INOUT_DESC_LEN)
+#endif
 
 #if CFG_TUSB_MCU == OPT_MCU_LPC175X_6X || CFG_TUSB_MCU == OPT_MCU_LPC177X_8X || CFG_TUSB_MCU == OPT_MCU_LPC40XX
   // LPC 17xx and 40xx endpoint type (bulk/interrupt/iso) are fixed by its number
@@ -134,7 +138,9 @@ enum
   ITF_NUM_CDC = 0,
   ITF_NUM_CDC_DATA,
   ITF_NUM_HID,
+#ifndef PRK_NO_MSC
   ITF_NUM_MSC,
+#endif
   ITF_NUM_TOTAL
 };
 
@@ -146,8 +152,10 @@ uint8_t const desc_fs_configuration[] =
   // Interface number, string index, EP notification address and size, EP data address (out, in) and size.
   TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 4, EPNUM_CDC_NOTIF, 8, EPNUM_CDC_OUT, EPNUM_CDC_IN, 64),
 
+#ifndef PRK_NO_MSC
   // Interface number, string index, EP Out & EP In address, EP size
   TUD_MSC_DESCRIPTOR(ITF_NUM_MSC, 5, EPNUM_MSC_OUT, EPNUM_MSC_IN, 64),
+#endif
 
   // Interface number, string index, protocol, report descriptor len, EP In address, size & polling interval
   TUD_HID_INOUT_DESCRIPTOR(ITF_NUM_HID, 0, 0, sizeof(desc_hid_report), EPNUM_HID_OUT, EPNUM_HID_IN, 64, 0x08),
diff --git a/src/version.h b/src/version.h
index 4652a730..c327e565 100644
--- a/src/version.h
+++ b/src/version.h
@@ -1,5 +1,5 @@
 #pragma once
 
 #define PRK_VERSION   "0.9.0"
-#define PRK_BUILDDATE "20210908"
-#define PRK_REVISION  "e8ee115"
+#define PRK_BUILDDATE "20210909"
+#define PRK_REVISION  "6e77670"

From a1b573fdfe35ec458a1f7da971f28b3728f7e3ac Mon Sep 17 00:00:00 2001
From: HASUMI Hitoshi <hasumikin@gmail.com>
Date: Fri, 10 Sep 2021 01:01:47 +0900
Subject: [PATCH 10/10] Improve cmake to make zip and gz

---
 CMakeLists.txt | 7 +++++++
 src/version.h  | 4 ++--
 2 files changed, 9 insertions(+), 2 deletions(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt
index bd8f7d82..70d3143f 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -122,3 +122,10 @@ include_directories(${PROJECT}
 
 # create map/bin/hex/uf2 file in addition to ELF.
 pico_add_extra_outputs(${PROJECT})
+
+add_custom_command(
+  TARGET ${PROJECT}
+  POST_BUILD
+  COMMAND gzip -k ${PROJECT}.uf2
+  COMMAND zip -r ${PROJECT}.uf2.zip ${PROJECT}.uf2
+)
diff --git a/src/version.h b/src/version.h
index c327e565..d7e3bbcf 100644
--- a/src/version.h
+++ b/src/version.h
@@ -1,5 +1,5 @@
 #pragma once
 
 #define PRK_VERSION   "0.9.0"
-#define PRK_BUILDDATE "20210909"
-#define PRK_REVISION  "6e77670"
+#define PRK_BUILDDATE "20210910"
+#define PRK_REVISION  "5a68976"