From 0ab91d6547cb15ec6002e288c1a0bd6610bbb8e2 Mon Sep 17 00:00:00 2001
From: Micah Elizabeth Scott <micah@scanlime.org>
Date: Wed, 9 Oct 2013 13:04:56 -0700
Subject: [PATCH] Support for DFU_DETACH. Bootloader is now usable!

---
 firmware/fadecandy.cpp | 26 +++++++++++++++-
 firmware/usb_desc.c    | 68 +++++++++++++++++-------------------------
 firmware/usb_desc.h    | 11 +++++--
 firmware/usb_dev.c     | 41 +++++++++++++++++++++++--
 firmware/usb_dev.h     |  5 ++++
 5 files changed, 104 insertions(+), 47 deletions(-)

diff --git a/firmware/fadecandy.cpp b/firmware/fadecandy.cpp
index 17da6c9..501fc95 100644
--- a/firmware/fadecandy.cpp
+++ b/firmware/fadecandy.cpp
@@ -38,6 +38,9 @@ static OctoWS2811z leds(LEDS_PER_STRIP, ledBuffer, WS2811_800kHz);
 // Residuals for temporal dithering
 static int8_t residual[CHANNELS_TOTAL];
 
+// Reserved RAM area for signalling entry to bootloader
+extern uint32_t boot_token;
+
 
 static inline uint32_t calculateInterpCoefficient()
 {
@@ -443,12 +446,30 @@ static void updateDrawBuffer(unsigned interpCoefficient)
     }
 }
 
+void dfu_reboot()
+{
+    // Reboot to the Fadecandy Bootloader
+    boot_token = 0x74624346;
+
+    // Short delay to allow the host to receive the response to DFU_DETACH.
+    uint32_t deadline = millis() + 10;
+    while (millis() < deadline) {
+        watchdog_refresh();
+    }
+
+    // Detach from USB, and use the watchdog to time out a 10ms USB disconnect.
+    __disable_irq();
+    USB0_CONTROL = 0;
+    while (1);
+}
+
 extern "C" int main()
 {
     pinMode(LED_BUILTIN, OUTPUT);
     leds.begin();
 
-    while (1) {
+    // Application main loop
+    while (usb_dfu_state == DFU_appIDLE) {
         watchdog_refresh();
 
         buffers.handleUSB();
@@ -460,4 +481,7 @@ extern "C" int main()
             memset(residual, 0, sizeof residual);
         }
     }
+
+    // Reboot into DFU bootloader
+    dfu_reboot();
 }
diff --git a/firmware/usb_desc.c b/firmware/usb_desc.c
index b8e0263..a9aa22b 100644
--- a/firmware/usb_desc.c
+++ b/firmware/usb_desc.c
@@ -132,6 +132,27 @@ static uint8_t config_descriptor[CONFIG_DESC_SIZE] = {
         0,                                      // bInterval
 #endif // FC_INTERFACE
 
+#ifdef DFU_INTERFACE
+        // interface descriptor, DFU Mode (DFU spec Table 4.4)
+        9,                                      // bLength
+        4,                                      // bDescriptorType
+        DFU_INTERFACE,                          // bInterfaceNumber
+        0,                                      // bAlternateSetting
+        0,                                      // bNumEndpoints
+        0xFE,                                   // bInterfaceClass
+        0x01,                                   // bInterfaceSubClass
+        0x01,                                   // bInterfaceProtocol (Runtime)
+        4,                                      // iInterface
+        // DFU Functional Descriptor (DFU spec TAble 4.2)
+        9,                                      // bLength
+        0x21,                                   // bDescriptorType
+        0x0D,                                   // bmAttributes
+        LSB(DFU_DETACH_TIMEOUT),                // wDetachTimeOut
+        MSB(DFU_DETACH_TIMEOUT),
+        LSB(DFU_TRANSFER_SIZE),                 // wTransferSize
+        MSB(DFU_TRANSFER_SIZE),
+        0x01,0x01,                              // bcdDFUVersion
+#endif // DFU_INTERFACE
 };
 
 
@@ -174,6 +195,11 @@ struct usb_string_descriptor_struct usb_string_product_name_default = {
         3,
         PRODUCT_NAME
 };
+struct usb_string_descriptor_struct usb_string_dfu_name = {
+        2 + DFU_NAME_LEN * 2,
+        3,
+        DFU_NAME
+};
 
 // 32-digit hex string, corresponding to the MK20DX128's built-in unique 128-bit ID.
 struct usb_string_descriptor_struct usb_string_serial_number_default = {
@@ -232,37 +258,11 @@ const usb_descriptor_list_t usb_descriptor_list[] = {
         //wValue, wIndex, address,          length
         {0x0100, 0x0000, device_descriptor, sizeof(device_descriptor)},
         {0x0200, 0x0000, config_descriptor, sizeof(config_descriptor)},
-#ifdef SEREMU_INTERFACE
-        {0x2200, SEREMU_INTERFACE, seremu_report_desc, sizeof(seremu_report_desc)},
-        {0x2100, SEREMU_INTERFACE, config_descriptor+SEREMU_DESC_OFFSET, 9},
-#endif
-#ifdef KEYBOARD_INTERFACE
-        {0x2200, KEYBOARD_INTERFACE, keyboard_report_desc, sizeof(keyboard_report_desc)},
-        {0x2100, KEYBOARD_INTERFACE, config_descriptor+KEYBOARD_DESC_OFFSET, 9},
-#endif
-#ifdef MOUSE_INTERFACE
-        {0x2200, MOUSE_INTERFACE, mouse_report_desc, sizeof(mouse_report_desc)},
-        {0x2100, MOUSE_INTERFACE, config_descriptor+MOUSE_DESC_OFFSET, 9},
-#endif
-#ifdef JOYSTICK_INTERFACE
-        {0x2200, JOYSTICK_INTERFACE, joystick_report_desc, sizeof(joystick_report_desc)},
-        {0x2100, JOYSTICK_INTERFACE, config_descriptor+JOYSTICK_DESC_OFFSET, 9},
-#endif
-#ifdef RAWHID_INTERFACE
-        {0x2200, RAWHID_INTERFACE, rawhid_report_desc, sizeof(rawhid_report_desc)},
-        {0x2100, RAWHID_INTERFACE, config_descriptor+RAWHID_DESC_OFFSET, 9},
-#endif
-#ifdef FLIGHTSIM_INTERFACE
-        {0x2200, FLIGHTSIM_INTERFACE, flightsim_report_desc, sizeof(flightsim_report_desc)},
-        {0x2100, FLIGHTSIM_INTERFACE, config_descriptor+FLIGHTSIM_DESC_OFFSET, 9},
-#endif
         {0x0300, 0x0000, (const uint8_t *)&string0, 0},
         {0x0301, 0x0409, (const uint8_t *)&usb_string_manufacturer_name, 0},
         {0x0302, 0x0409, (const uint8_t *)&usb_string_product_name, 0},
         {0x0303, 0x0409, (const uint8_t *)&usb_string_serial_number, 0},
-        //{0x0301, 0x0409, (const uint8_t *)&string1, 0},
-        //{0x0302, 0x0409, (const uint8_t *)&string2, 0},
-        //{0x0303, 0x0409, (const uint8_t *)&string3, 0},
+        {0x0304, 0x0409, (const uint8_t *)&usb_string_dfu_name, 0},
         {0, 0, NULL, 0}
 };
 
@@ -271,20 +271,6 @@ const usb_descriptor_list_t usb_descriptor_list[] = {
 //   Endpoint Configuration
 // **************************************************************
 
-#if 0
-// 0x00 = not used
-// 0x19 = Recieve only
-// 0x15 = Transmit only
-// 0x1D = Transmit & Recieve
-// 
-const uint8_t usb_endpoint_config_table[NUM_ENDPOINTS] = 
-{
-        0x00, 0x15, 0x19, 0x15, 0x00, 0x00, 0x00, 0x00, 
-        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
-};
-#endif
-
-
 const uint8_t usb_endpoint_config_table[NUM_ENDPOINTS] = 
 {
 #if (defined(ENDPOINT1_CONFIG) && NUM_ENDPOINTS >= 1)
diff --git a/firmware/usb_desc.h b/firmware/usb_desc.h
index bc2231f..81efa8d 100644
--- a/firmware/usb_desc.h
+++ b/firmware/usb_desc.h
@@ -82,19 +82,24 @@ let me know?  http://forum.pjrc.com/forums/4-Suggestions-amp-Bug-Reports
 */
 
 #define USB_FADECANDY
-  #define DEVICE_VER                0x0101
+  #define DEVICE_VER                0x0102
   #define DEVICE_CLASS              0xff      // Vendor specific
   #define MANUFACTURER_NAME         {'s','c','a','n','l','i','m','e'}
   #define MANUFACTURER_NAME_LEN     8
   #define PRODUCT_NAME              {'F','a','d','e','c','a','n','d','y'}
   #define PRODUCT_NAME_LEN          9
+  #define DFU_NAME                  {'F','a','d','e','c','a','n','d','y',' ','B','o','o','t','l','o','a','d','e','r'}
+  #define DFU_NAME_LEN              20
   #define EP0_SIZE                  64
   #define NUM_ENDPOINTS             1
-  #define NUM_INTERFACE             1
+  #define NUM_INTERFACE             2
   #define FC_INTERFACE              0
   #define FC_OUT_ENDPOINT           1
   #define FC_OUT_SIZE               64
-  #define CONFIG_DESC_SIZE          (9+9+7)
+  #define DFU_INTERFACE             1
+  #define DFU_DETACH_TIMEOUT        10000     // 10 seconds
+  #define DFU_TRANSFER_SIZE         1024      // Flash sector size
+  #define CONFIG_DESC_SIZE          (9+9+7+9+9)
   #define ENDPOINT1_CONFIG          ENDPOINT_RECEIVE_ONLY
 
 // NUM_ENDPOINTS = number of non-zero endpoints (0 to 15)
diff --git a/firmware/usb_dev.c b/firmware/usb_dev.c
index 84676df..3a67abc 100644
--- a/firmware/usb_dev.c
+++ b/firmware/usb_dev.c
@@ -1,4 +1,8 @@
-/* Teensyduino Core Library
+/* 
+ * Fadecandy firmware
+ * Copyright (c) 2013 Micah Elizabeth Scott
+ *
+ * Teensyduino Core Library
  * http://www.pjrc.com/teensy/
  * Copyright (c) 2013 PJRC.COM, LLC.
  *
@@ -123,7 +127,7 @@ static uint8_t ep0_tx_data_toggle = 0;
 uint8_t usb_rx_memory_needed = 0;
 
 volatile uint8_t usb_configuration = 0;
-
+volatile uint8_t usb_dfu_state = DFU_appIDLE;
 
 static void endpoint0_stall(void)
 {
@@ -284,6 +288,39 @@ static void usb_setup(void)
         endpoint0_stall();
         return;
 
+      case 0x03a1: // DFU_GETSTATUS
+        if (setup.wIndex != DFU_INTERFACE) {
+            endpoint0_stall();
+            return;
+        }
+        reply_buffer[0] = 0;    // bStatus = OK
+        reply_buffer[1] = 1;    // bwPollTimeout LSB = 1
+        reply_buffer[2] = 0;    // bwPollTimeout
+        reply_buffer[3] = 0;    // bwPollTimeout
+        reply_buffer[4] = usb_dfu_state;
+        reply_buffer[5] = 0;    // iString = 0
+        data = reply_buffer;
+        datalen = 6;
+        break;
+
+      case 0x05a1: // DFU_GETSTATE
+        if (setup.wIndex != DFU_INTERFACE) {
+            endpoint0_stall();
+            return;
+        }
+        reply_buffer[0] = usb_dfu_state;
+        data = reply_buffer;
+        datalen = 1;
+        break;
+
+      case 0x0021: // DFU_DETACH
+        if (setup.wIndex != DFU_INTERFACE) {
+            endpoint0_stall();
+            return;
+        }
+        usb_dfu_state = DFU_appDETACH;
+        break;
+
       default:
         endpoint0_stall();
         return;
diff --git a/firmware/usb_dev.h b/firmware/usb_dev.h
index 5122504..e22c2f9 100644
--- a/firmware/usb_dev.h
+++ b/firmware/usb_dev.h
@@ -53,6 +53,11 @@ void usb_tx(uint32_t endpoint, usb_packet_t *packet);
 void usb_tx_isr(uint32_t endpoint, usb_packet_t *packet);
 
 extern volatile uint8_t usb_configuration;
+extern volatile uint8_t usb_dfu_state;
+
+// DFU states
+#define DFU_appIDLE    0
+#define DFU_appDETACH  1
 
 extern uint16_t usb_rx_byte_count_data[NUM_ENDPOINTS];
 static inline uint32_t usb_rx_byte_count(uint32_t endpoint) __attribute__((always_inline));
-- 
GitLab