From 5126039a1ca9a7715a06761a714d3e4e9d158e11 Mon Sep 17 00:00:00 2001
From: Vegard Storheil Eriksen <zyp@jvnv.net>
Date: Sat, 26 Oct 2013 14:43:35 +0200
Subject: Changed bootloader to use HID instead of DFU.

---
 bootloader.cpp | 185 ++++++++++++++++++++-------------------------------------
 hidapi.py      |  17 ++++++
 hidflash.py    |  61 +++++++++++++++++++
 3 files changed, 143 insertions(+), 120 deletions(-)
 create mode 100644 hidapi.py
 create mode 100755 hidflash.py

diff --git a/bootloader.cpp b/bootloader.cpp
index fa79d9b..3e0365e 100644
--- a/bootloader.cpp
+++ b/bootloader.cpp
@@ -8,25 +8,51 @@
 #include <usb/hid.h>
 #include <usb/dfu.h>
 
+static uint32_t& reset_reason = *(uint32_t*)0x10000000;
+static const uint32_t* firmware_vtors = (uint32_t*)0x8002000;
+
+static bool do_reset;
+
 void reset() {
 	SCB.AIRCR = (0x5fa << 16) | (1 << 2); // SYSRESETREQ
 }
 
+void chainload(uint32_t offset) {
+	SCB.VTOR = offset;
+	
+	asm volatile("ldr sp, [%0]; ldr %0, [%0, #4]; bx %0" :: "r" (offset));
+}
+
+auto report_desc = pack(
+		usage_page(0xff55),
+		usage(0xb007),
+		collection(Collection::Application,
+			logical_minimum(0),
+			logical_maximum(255),
+			report_size(8),
+			report_count(1),
+			
+			input(0x02), // Status
+			
+			feature(0x02), // Function
+			
+			report_count(64),
+			output(0x02) // Data
+		)
+);
+
 auto dev_desc = device_desc(0x200, 0, 0, 0, 64, 0x1d50, 0x6084, 0, 0, 0, 0, 1);
 auto conf_desc = configuration_desc(1, 1, 0, 0xc0, 0,
-	interface_desc(0, 0, 0, 0xfe, 0x01, 0x02, 0,
-		dfu_functional_desc(0x0d, 0, 64, 0x110)
-	)
 	// HID interface.
-	//interface_desc(1, 0, 1, 0x03, 0x00, 0x00, 0,
-	//	hid_desc(0x111, 0, 1, 0x22, sizeof(report_desc)),
-	//	endpoint_desc(0x81, 0x03, 16, 1)
-	//)
+	interface_desc(0, 0, 1, 0x03, 0x00, 0x00, 0,
+		hid_desc(0x111, 0, 1, 0x22, sizeof(report_desc)),
+		endpoint_desc(0x81, 0x03, 64, 1)
+	)
 );
 
 desc_t dev_desc_p = {sizeof(dev_desc), (void*)&dev_desc};
 desc_t conf_desc_p = {sizeof(conf_desc), (void*)&conf_desc};
-//desc_t report_desc_p = {sizeof(report_desc), (void*)&report_desc};
+desc_t report_desc_p = {sizeof(report_desc), (void*)&report_desc};
 
 static Pin usb_dm = GPIOA[11];
 static Pin usb_dp = GPIOA[12];
@@ -111,114 +137,48 @@ class Flashloader {
 
 Flashloader flashloader;
 
-class USB_DFU : public USB_class_driver {
-	private:
-		USB_generic& usb;
-		
-		uint8_t state;
-		
-		bool get_status(uint16_t wValue, uint16_t wIndex, uint16_t wLength) {
-			if(wLength > 6) {
-				wLength = 6;
-			}
-			
-			uint8_t buf[] = {0, 0, 0, 0, state, 0};
-			
-			usb.write(0, (uint32_t*)buf, wLength);
-			
-			return true;
-		}
-		
-		bool download(uint16_t wValue, uint16_t wIndex, uint16_t wLength) {
-			if(!wLength) {
-				state = 2;
-				
-				if(!flashloader.finish()) {
-					return false;
-				}
-				
-				usb.write(0, nullptr, 0);
-				
-				return true;
-			}
-			
-			if(state == 2) {
-				state = 5;
-				
-				return flashloader.prepare();
-			}
-			
-			return true;
-		}
-		
+class HID_bootloader : public USB_HID {
 	public:
-		USB_DFU(USB_generic& usbd) : usb(usbd), state(2) {
-			usb.register_driver(this);
-		}
+		HID_bootloader(USB_generic& usbd, desc_t rdesc) : USB_HID(usbd, rdesc, 0, 1, 64) {}
 	
 	protected:
-		virtual SetupStatus handle_setup(uint8_t bmRequestType, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, uint16_t wLength) {
-			// DFU_GETSTATUS
-			if(bmRequestType == 0xa1 && bRequest == 0x03) {
-				return get_status(wValue, wIndex, wLength) ? SetupStatus::Ok : SetupStatus::Stall;
-			}
-			
-			// DFU_CLRSTATUS
-			// DFU_GETSTATE
-			// DFU_ABORT
-			
-			// DFU_DNLOAD
-			if(bmRequestType == 0x21 && bRequest == 0x01) {
-				return download(wValue, wIndex, wLength) ? SetupStatus::Ok : SetupStatus::Stall;
+		virtual bool set_output_report(uint32_t* buf, uint32_t len) {
+			if(len != 64) {
+				return false;
 			}
 			
-			return SetupStatus::Unhandled;
+			return flashloader.write_block(len, buf);
 		}
 		
-		virtual void handle_out(uint8_t ep, uint32_t len) {
-			if(ep != 0 || len == 0) {
-				return;
+		virtual bool set_feature_report(uint32_t* buf, uint32_t len) {
+			if(len != 1) {
+				return false;
 			}
 			
-			uint32_t buf[16];
-			usb.read(ep, buf, len);
-			
-			if(state == 5) {
-				flashloader.write_block(len, buf);
+			switch(*buf & 0xff) {
+				case 0:
+					return true;
+				
+				case 0x10: // Reset to bootloader
+					return false; // Not available in bootloader mode
+				
+				case 0x11: // Reset to runtime
+					do_reset = true;
+					return true;
+				
+				case 0x20: // Flash prepare
+					return flashloader.prepare();
+				
+				case 0x21: // Flash finish
+					return flashloader.finish();
+				
+				default:
+					return false;
 			}
-			
-			usb.write(0, nullptr, 0);
-		}
-};
-
-USB_DFU usb_dfu(usb);
-
-/*
-uint32_t last_led_time;
-
-class HID_arcin : public USB_HID {
-	public:
-		HID_arcin(USB_generic& usbd, desc_t rdesc) : USB_HID(usbd, rdesc, 1, 1, 64) {}
-	
-	protected:
-		virtual bool set_output_report(uint32_t* buf, uint32_t len) {
-			last_led_time = Time::time();
-			button_leds.set(*buf);
-			return true;
 		}
 };
 
-HID_arcin usb_hid(usb, report_desc_p);
-*/
-
-void chainload(uint32_t offset) {
-	SCB.VTOR = offset;
-	
-	asm volatile("ldr sp, [%0]; ldr %0, [%0, #4]; bx %0" :: "r" (offset));
-}
-
-uint32_t& reset_reason = *(uint32_t*)0x10000000;
-uint32_t* firmware_vtors = (uint32_t*)0x8002000;
+HID_bootloader usb_hid(usb, report_desc_p);
 
 bool normal_boot() {
 	// Check if this was a reset-to-bootloader.
@@ -280,26 +240,11 @@ int main() {
 	while(1) {
 		usb.process();
 		
-		if(~button_inputs.get() & (1 << 5)) {
+		if(do_reset) {
+			Time::sleep(10);
 			reset();
 		}
 		
 		GPIOC[0].set(Time::time() & 512);
-		
-		/*
-		usb.process();
-		
-		uint16_t buttons = button_inputs.get() ^ 0x7ff;
-		
-		if(Time::time() - last_led_time > 1000) {
-			button_leds.set(buttons);
-		}
-		
-		if(usb.ep_ready(1)) {
-			report_t report = {buttons, uint8_t(TIM2.CNT), uint8_t(TIM3.CNT)};
-			
-			usb.write(1, (uint32_t*)&report, sizeof(report));
-		}
-		*/
 	}
 }
diff --git a/hidapi.py b/hidapi.py
new file mode 100644
index 0000000..22f94d8
--- /dev/null
+++ b/hidapi.py
@@ -0,0 +1,17 @@
+import ctypes, ctypes.util
+
+path = ctypes.util.find_library('hidapi')
+
+if not path:
+	raise ImportError('Cannot find hidapi library')
+
+hidapi = ctypes.CDLL(path)
+
+hidapi.hid_open.argtypes = [ctypes.c_ushort, ctypes.c_ushort, ctypes.c_wchar_p]
+hidapi.hid_open.restype = ctypes.c_void_p
+
+hidapi.hid_read_timeout.argtypes = [ctypes.c_void_p, ctypes.c_char_p, ctypes.c_size_t, ctypes.c_int]
+hidapi.hid_read.argtypes = [ctypes.c_void_p, ctypes.c_char_p, ctypes.c_size_t]
+hidapi.hid_write.argtypes = [ctypes.c_void_p, ctypes.c_char_p, ctypes.c_size_t]
+hidapi.hid_send_feature_report.argtypes = [ctypes.c_void_p, ctypes.c_char_p, ctypes.c_size_t]
+hidapi.hid_get_feature_report.argtypes = [ctypes.c_void_p, ctypes.c_char_p, ctypes.c_size_t]
diff --git a/hidflash.py b/hidflash.py
new file mode 100755
index 0000000..a7042ca
--- /dev/null
+++ b/hidflash.py
@@ -0,0 +1,61 @@
+#!/usr/bin/env python
+
+from hidapi import hidapi
+from elftools.elf.elffile import ELFFile
+
+import ctypes, time, sys
+
+e = ELFFile(open(sys.argv[1]))
+
+buf = ''
+
+for segment in sorted(e.iter_segments(), key = lambda x: x.header.p_paddr):
+	if segment.header.p_type != 'PT_LOAD':
+		continue
+	
+	data = segment.data()
+	lma = segment.header.p_paddr
+	
+	# Workaround for LD aligning segments to a larger boundary than 8k.
+	if lma == 0x8000000:
+		lma += 0x2000
+		data = data[0x2000:]
+	
+	# Add padding if necessary.
+	buf += '\0' * (lma - 0x8002000 - len(buf))
+	
+	buf += data
+
+# Align to 64B
+if len(buf) & (64 - 1):
+	buf += '\0' * (64 - (len(buf) & (64 - 1)))
+
+# Open device
+dev = hidapi.hid_open(0x1d50, 0x6084, None)
+
+if not dev:
+	raise RuntimeError('Device not found.')
+
+print 'Found device, starting flashing.'
+
+# Prepare
+if hidapi.hid_send_feature_report(dev, ctypes.c_char_p('\x00\x20'), 2) != 2:
+	raise RuntimeError('Prepare failed.')
+
+# Flash
+while buf:
+	if hidapi.hid_write(dev, ctypes.c_char_p('\x00' + buf[:64]), 65) != 65:
+		raise RuntimeError('Writing failed.')
+	buf = buf[64:]
+
+# Finish
+if hidapi.hid_send_feature_report(dev, ctypes.c_char_p('\x00\x21'), 2) != 2:
+	raise RuntimeError('Finish failed.')
+
+print 'Flashing finished, resetting.'
+
+# Reset
+if hidapi.hid_send_feature_report(dev, ctypes.c_char_p('\x00\x11'), 2) != 2:
+	raise RuntimeError('Reset failed.')
+
+print 'Done, everything ok.'
-- 
cgit v1.2.3