works now
authorNIIBE Yutaka <gniibe@fsij.org>
Mon, 11 Jul 2011 04:24:29 +0000 (13:24 +0900)
committerNIIBE Yutaka <gniibe@fsij.org>
Mon, 11 Jul 2011 04:25:40 +0000 (13:25 +0900)
.gitignore
src/main.c
src/neug.ld.in
tool/dfuse.py [new file with mode: 0755]
tool/intel_hex.py [new file with mode: 0644]
tool/openocd-script/lock.tcl [new file with mode: 0644]
tool/openocd-script/options_read.tcl [new file with mode: 0644]
tool/openocd-script/unlock.tcl [new file with mode: 0644]
tool/openocd-script/write.tcl [new file with mode: 0644]

index d79de36..dbf2b92 100644 (file)
@@ -1,2 +1,3 @@
 *.o
 *.lst
+*.pyc
index 5636565..535650f 100644 (file)
 #include "board.h"
 #include "usb_cdc.h"
 
+/*
+ * We are trying to avoid dependency to C library. 
+ * GCC built-in functions are declared here.
+ */
+void *memcpy(void *dest, const void *src, size_t n);
+
 /*
  * USB Driver structure.
  */
@@ -92,7 +98,7 @@ static const uint8_t vcom_configuration_descriptor_data[67] = {
   USB_DESC_BYTE         (0x24),         /* bDescriptorType (CS_INTERFACE).  */
   USB_DESC_BYTE         (0x01),         /* bDescriptorSubtype (Call Management
                                            Functional Descriptor).          */
-  USB_DESC_BYTE         (0x00),         /* bmCapabilities (D0+D1).          */
+  USB_DESC_BYTE         (0x03),         /* bmCapabilities (D0+D1).          */
   USB_DESC_BYTE         (0x01),         /* bDataInterface.                  */
   /* ACM Functional Descriptor.*/
   USB_DESC_BYTE         (4),            /* bFunctionLength.                 */
@@ -263,9 +269,6 @@ static const USBEndpointConfig ep3config = {
   NULL
 };
 
-#define CONFIGURED 1
-uint8_t bDeviceState;
-
 /*
  * Handles the USB driver global events.
  */
@@ -281,7 +284,6 @@ static void usb_event(USBDriver *usbp, usbevent_t event) {
        Note, this callback is invoked from an ISR so I-Class functions
        must be used.*/
     chSysLockFromIsr();
-    bDeviceState = CONFIGURED;
     usbInitEndpointI(usbp, DATA_REQUEST_EP, &ep1config);
     usbInitEndpointI(usbp, INTERRUPT_REQUEST_EP, &ep2config);
     usbInitEndpointI(usbp, DATA_AVAILABLE_EP, &ep3config);
@@ -310,17 +312,9 @@ static const SerialUSBConfig serusbcfg = {
     NULL
   }
 };
-\f
-/*
- * main thread does 1-bit LED display output
- */
-#define LED_TIMEOUT_INTERVAL   MS2ST(100)
-#define LED_TIMEOUT_ZERO       MS2ST(50)
-#define LED_TIMEOUT_ONE                MS2ST(200)
-#define LED_TIMEOUT_STOP       MS2ST(500)
 
 Thread *main_thread;
-\f
+
 /* Total number of channels to be sampled by a single ADC operation.*/
 #define ADC_GRP1_NUM_CHANNELS   2
  
@@ -341,8 +335,8 @@ static void adccb (ADCDriver *adcp, adcsample_t *buffer, size_t n);
 /*
  * ADC conversion group.
  * Mode:        Linear buffer, 4 samples of 2 channels, SW triggered.
- * Channels:    Vref   (239.5 cycles sample time)
- *              Sensor (239.5 cycles sample time)
+ * Channels:    Vref   (1.5 cycles sample time, violating the spec.)
+ *              Sensor (1.5 cycles sample time, violating the spec.)
  */
 static const ADCConversionGroup adcgrpcfg = {
   FALSE,
@@ -442,7 +436,6 @@ static uint32_t ep_value (void)
  * Entry point.
  *
  * NOTE: the main function is already a thread in the system on entry.
- *       See the hwinit1_common function.
  */
 int
 main (int argc, char **argv)
@@ -455,6 +448,7 @@ main (int argc, char **argv)
   halInit();
   chSysInit();
 
+  main_thread = chThdSelf ();
 
   /*
    * Activates the USB driver and then the USB bus pull-up on D+.
@@ -463,8 +457,6 @@ main (int argc, char **argv)
   sduStart(&SDU1, &serusbcfg);
   USB_Cable_Config (ENABLE);
 
-  main_thread = chThdSelf ();
-
   adcStart (&ADCD1, NULL);
   adcStartConversion (&ADCD1, &adcgrpcfg, samp, ADC_GRP1_BUF_DEPTH);
 
@@ -474,9 +466,6 @@ main (int argc, char **argv)
 
       count++;
 
-      if (bDeviceState != CONFIGURED)
-       continue;
-
       m = chEvtWaitOne (ALL_EVENTS);
 
       if (m == (eventmask_t)1)
@@ -510,12 +499,14 @@ main (int argc, char **argv)
 
              memcpy (s + (r&7)*4, (const char *)&x, 4);
              r = (r + 1) & 7;
-             if (r == 0)
+             if (r == 0 && SDU1.config->usbp->state == USB_ACTIVE)
                {
                  static uint8_t led;
 
                  led++;
                  set_led ((led & 0x80) == 0);
+
+                 chIQResetI (&(SDU1.iqueue)); /* Ignore input */
                  chIOWriteTimeout (&SDU1, (uint8_t *)s, 32, TIME_INFINITE);
                }
 
@@ -531,5 +522,7 @@ void
 fatal (uint8_t code)
 {
   fatal_code = code;
+
+  set_led (1);
   for (;;);
 }
index cf344a6..7f61655 100644 (file)
@@ -48,7 +48,8 @@ SECTIONS
     startup : ALIGN(16) SUBALIGN(16)
     {
         KEEP(*(vectors))
-    } > flash
+        . = ALIGN (16);
+    } > flash =0xffffffff
 
     constructors : ALIGN(4) SUBALIGN(4)
     {
@@ -117,6 +118,11 @@ SECTIONS
         . = ALIGN(4);
         PROVIDE(_bss_end = .);
     } > ram
+
+    .fill_ffff :
+    {
+        . = ALIGN (@FLASH_PAGE_SIZE@);
+    } > flash =0xffffffff
 }
 
 PROVIDE(end = .);
diff --git a/tool/dfuse.py b/tool/dfuse.py
new file mode 100755 (executable)
index 0000000..722f217
--- /dev/null
@@ -0,0 +1,348 @@
+#! /usr/bin/python
+
+"""
+dfuse.py - DFU (Device Firmware Upgrade) tool for STM32 Processor.
+"SE" in DfuSe stands for "STmicroelectronics Extention".
+
+Copyright (C) 2010 Free Software Initiative of Japan
+Author: NIIBE Yutaka <gniibe@fsij.org>
+
+This file is a part of Gnuk, a GnuPG USB Token implementation.
+
+Gnuk is free software: you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+Gnuk is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
+License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+"""
+
+from intel_hex import *
+import sys, time, struct
+
+# INPUT: intel hex file
+
+# As of October 2010 (DfuSe V3.0.1 - 06/18/2010), it seems that
+# following features are not supported by DfuSe implementation on
+# target:
+#
+#     unprotect
+#     leave_dfu_mode
+#     write to option bytes
+#     erase for mass erase
+
+
+# See: AN3156 by STMicroelectronics
+
+import usb
+
+# check string descriptor in interrface descriptor in config descriptor: iInterface
+
+# USB DFU class, subclass, protocol
+DFU_CLASS = 0xFE
+DFU_SUBCLASS = 0x01
+DFU_STM32PROTOCOL_0 = 0
+DFU_STM32PROTOCOL_2 = 2
+
+# DFU request
+DFU_DETACH    = 0x00
+DFU_DNLOAD    = 0x01
+DFU_UPLOAD    = 0x02
+DFU_GETSTATUS = 0x03
+DFU_CLRSTATUS = 0x04
+DFU_GETSTATE  = 0x05
+DFU_ABORT     = 0x06
+
+# DFU status
+DFU_STATUS_OK                 = 0x00
+DFU_STATUS_ERROR_TARGET       = 0x01
+DFU_STATUS_ERROR_FILE         = 0x02
+DFU_STATUS_ERROR_WRITE        = 0x03
+DFU_STATUS_ERROR_ERASE        = 0x04
+DFU_STATUS_ERROR_CHECK_ERASED = 0x05
+DFU_STATUS_ERROR_PROG         = 0x06
+DFU_STATUS_ERROR_VERIFY       = 0x07
+DFU_STATUS_ERROR_ADDRESS      = 0x08
+DFU_STATUS_ERROR_NOTDONE      = 0x09
+DFU_STATUS_ERROR_FIRMWARE     = 0x0a
+DFU_STATUS_ERROR_VENDOR       = 0x0b
+DFU_STATUS_ERROR_USBR         = 0x0c
+DFU_STATUS_ERROR_POR          = 0x0d
+DFU_STATUS_ERROR_UNKNOWN      = 0x0e
+DFU_STATUS_ERROR_STALLEDPKT   = 0x0f
+
+# DFU state
+STATE_APP_IDLE                = 0x00
+STATE_APP_DETACH              = 0x01
+STATE_DFU_IDLE                = 0x02
+STATE_DFU_DOWNLOAD_SYNC       = 0x03
+STATE_DFU_DOWNLOAD_BUSY       = 0x04
+STATE_DFU_DOWNLOAD_IDLE       = 0x05
+STATE_DFU_MANIFEST_SYNC       = 0x06
+STATE_DFU_MANIFEST            = 0x07
+STATE_DFU_MANIFEST_WAIT_RESET = 0x08
+STATE_DFU_UPLOAD_IDLE         = 0x09
+STATE_DFU_ERROR               = 0x0a
+
+# Return tuple of 4-bytes from integer
+def get_four_bytes (v):
+    return [ v % 256, (v >> 8)%256, (v >> 16)%256, (v >> 24) ]
+
+class DFU_STM32:
+    def __init__(self, device, configuration, interface):
+        """
+        __init__(device, configuration, interface) -> None
+        Initialize the device.
+        device: usb.Device object.
+        configuration: configuration number.
+        interface: usb.Interface object representing the interface and altenate setting.
+        """
+        if interface.interfaceClass != DFU_CLASS:
+            raise ValueError, "Wrong interface class"
+        if interface.interfaceSubClass != DFU_SUBCLASS:
+            raise ValueError, "Wrong interface sub class"
+        self.__protocol = interface.interfaceProtocol
+        self.__devhandle = device.open()
+        self.__devhandle.setConfiguration(configuration)
+        self.__devhandle.claimInterface(interface)
+        self.__devhandle.setAltInterface(interface)
+
+        self.__intf = interface.interfaceNumber
+        self.__alt = interface.alternateSetting
+        self.__conf = configuration
+        # Initialize members
+        self.__blocknum = 0
+
+    def ll_getdev(self):
+        return self.__devhandle
+
+    def ll_get_string(self, index):
+        # specify buffer length for 80
+        return self.__devhandle.getString(index, 80)
+
+    def ll_get_status(self):
+        # Status, PollTimeout[3], State, String
+        return self.__devhandle.controlMsg(requestType = 0xa1,
+                                           request = DFU_GETSTATUS,
+                                           value = 0,
+                                           index = self.__intf,
+                                           buffer = 6,
+                                           timeout = 3000000)
+
+    def ll_clear_status(self):
+        return self.__devhandle.controlMsg(requestType = 0x21,
+                                           request = DFU_CLRSTATUS,
+                                           value = 0,
+                                           index = self.__intf,
+                                           buffer = None)
+
+    # Upload: TARGET -> HOST
+    def ll_upload_block(self, block_num):
+        return self.__devhandle.controlMsg(requestType = 0xa1,
+                                           request = DFU_UPLOAD,
+                                           value = block_num,
+                                           index = self.__intf,
+                                           buffer = 1024,
+                                           timeout = 3000000)
+
+    # Download: HOST -> TARGET
+    def ll_download_block(self, block_num, block):
+        return self.__devhandle.controlMsg(requestType = 0x21,
+                                           request = DFU_DNLOAD,
+                                           value = block_num,
+                                           index = self.__intf,
+                                           buffer = block)
+
+    def dfuse_read_memory(self):
+        blocknum = self.__blocknum
+        self.__blocknum = self.__blocknum + 1
+        try:
+            block = self.ll_upload_block(blocknum)
+            return block
+        except:
+            s = self.ll_get_status()
+            while s[4] == STATE_DFU_DOWNLOAD_BUSY:
+                time.sleep(0.1)
+                s = self.ll_get_status()
+            raise ValueError, "Read memory failed (%d)" % s[0]
+
+    def dfuse_set_address_pointer(self, address):
+        bytes = get_four_bytes (address)
+        self.__blocknum = 2
+        self.ll_download_block(0, [0x21] + bytes)
+        s = self.ll_get_status()
+        while s[4] == STATE_DFU_DOWNLOAD_BUSY:
+            time.sleep(0.1)
+            s = self.ll_get_status()
+        if s[4] != STATE_DFU_DOWNLOAD_IDLE:
+            raise ValueError, "Set Address Pointer failed"
+
+    def dfuse_erase(self, address):
+        bytes = get_four_bytes (address)
+        self.ll_download_block(0, [0x41] + bytes)
+        s = self.ll_get_status()
+        while s[4] == STATE_DFU_DOWNLOAD_BUSY:
+            time.sleep(0.1)
+            s = self.ll_get_status()
+        if s[4] != STATE_DFU_DOWNLOAD_IDLE:
+            raise ValueError, "Erase failed"
+
+    def dfuse_write_memory(self, block):
+        blocknum = self.__blocknum
+        self.__blocknum = self.__blocknum + 1
+        self.ll_download_block(blocknum, block)
+        s = self.ll_get_status()
+        while s[4] == STATE_DFU_DOWNLOAD_BUSY:
+            time.sleep(0.1)
+            s = self.ll_get_status()
+        if s[4] != STATE_DFU_DOWNLOAD_IDLE:
+            raise ValueError, "Write memory failed"
+
+    def download(self, ih):
+        # First, erase pages
+        sys.stdout.write("Erasing: ")
+        sys.stdout.flush()
+        for start_addr in sorted(ih.memory.keys()):
+            data = ih.memory[start_addr]
+            end_addr = start_addr + len(data)
+            addr = start_addr & 0xfffffc00
+            i = 0
+            while addr < end_addr:
+                self.dfuse_erase(addr)
+                if i & 0x03 == 0x03:
+                    sys.stdout.write("#")
+                    sys.stdout.flush()
+                addr += 1024
+                i += 1
+        sys.stdout.write("\n")
+        sys.stdout.flush()
+        # Then, write pages
+        sys.stdout.write("Writing: ")
+        sys.stdout.flush()
+        for start_addr in sorted(ih.memory.keys()):
+            data = ih.memory[start_addr]
+            end_addr = start_addr + len(data)
+            addr = start_addr & 0xfffffc00
+            # XXX: data should be 1-KiB aligned
+            if addr != start_addr:
+                raise ValueError, "padding is not supported yet"
+            self.dfuse_set_address_pointer(addr)
+            i = 0
+            while addr < end_addr:
+                self.dfuse_write_memory(data[i*1024:(i+1)*1024])
+                if i & 0x03 == 0x03:
+                    sys.stdout.write("#")
+                    sys.stdout.flush()
+                addr += 1024
+                i += 1
+        if self.__protocol == DFU_STM32PROTOCOL_0:
+            # 0-length write at the end
+            self.ll_download_block(self.__blocknum, None)
+            s = self.ll_get_status()
+            if s[4] == STATE_DFU_MANIFEST:
+                time.sleep(1)
+                try:
+                    s = self.ll_get_status()
+                except:
+                    self.__devhandle.reset()
+            elif s[4] == STATE_DFU_MANIFEST_WAIT_RESET:
+                self.__devhandle.reset()
+            elif s[4] != STATE_DFU_IDLE:
+                raise ValueError, "write failed (%d)." % s[4]
+        else:
+            self.ll_clear_status()
+            self.ll_clear_status()
+        sys.stdout.write("\n")
+        sys.stdout.flush()
+
+    def verify(self, ih):
+        s = self.ll_get_status()
+        if s[4] != STATE_DFU_IDLE:
+            self.ll_clear_status()
+        # Read pages
+        sys.stdout.write("Reading: ")
+        sys.stdout.flush()
+        for start_addr in sorted(ih.memory.keys()):
+            data = ih.memory[start_addr]
+            end_addr = start_addr + len(data)
+            addr = start_addr & 0xfffffc00
+            # XXX: data should be 1-KiB aligned
+            if addr != start_addr:
+                raise ValueError, "padding is not supported yet"
+            self.dfuse_set_address_pointer(addr)
+            self.ll_clear_status()
+            self.ll_clear_status()
+            i = 0
+            while addr < end_addr:
+                block = self.dfuse_read_memory()
+                j = 0
+                for c in data[i*1024:(i+1)*1024]:
+                    if (ord(c)&0xff) != block[j]:
+                        raise ValueError, "verify failed at %08x" % (addr + i*1024+j)
+                    j += 1
+                if i & 0x03 == 0x03:
+                    sys.stdout.write("#")
+                    sys.stdout.flush()
+                addr += 1024
+                i += 1
+            self.ll_clear_status()
+            self.ll_clear_status()
+        self.ll_clear_status()
+        sys.stdout.write("\n")
+        sys.stdout.flush()
+
+busses = usb.busses()
+
+# 0483: SGS Thomson Microelectronics
+# df11: DfuSe
+USB_VENDOR_STMICRO=0x0483
+USB_PRODUCT_DFUSE=0xdf11
+
+def get_device():
+    for bus in busses:
+        devices = bus.devices
+        for dev in devices:
+            if dev.idVendor != USB_VENDOR_STMICRO:
+                continue
+            if dev.idProduct != USB_PRODUCT_DFUSE:
+                continue
+            for config in dev.configurations:
+                for intf in config.interfaces:
+                    for alt in intf:
+                        if alt.interfaceClass == DFU_CLASS and \
+                                alt.interfaceSubClass == DFU_SUBCLASS and \
+                                (alt.interfaceProtocol == DFU_STM32PROTOCOL_0 or \
+                                     alt.interfaceProtocol == DFU_STM32PROTOCOL_2):
+                            return dev, config, alt
+    raise ValueError, "Device not found"
+
+def main(filename):
+    dev, config, intf = get_device()
+    print "Device:", dev.filename
+    print "Configuration", config.value
+    print "Interface", intf.interfaceNumber
+    dfu = DFU_STM32(dev, config, intf)
+    print dfu.ll_get_string(intf.iInterface)
+    s = dfu.ll_get_status()
+    if s[4] == STATE_DFU_ERROR:
+        dfu.ll_clear_status()
+    s = dfu.ll_get_status()
+    print s
+    if s[4] == STATE_DFU_IDLE:
+        exit
+    transfer_size = 1024
+    if s[0] != DFU_STATUS_OK:
+        print s
+        exit
+    ih = intel_hex(filename)
+    dfu.download(ih)
+    dfu.verify(ih)
+
+if __name__ == '__main__':
+    main(sys.argv[1])
diff --git a/tool/intel_hex.py b/tool/intel_hex.py
new file mode 100644 (file)
index 0000000..a2b0e28
--- /dev/null
@@ -0,0 +1,94 @@
+"""
+intel_hex.py - Intel Hex file reader.
+
+Copyright (C) 2010 Free Software Initiative of Japan
+Author: NIIBE Yutaka <gniibe@fsij.org>
+
+You can use/distribute/modify/etc. this for any purpose.
+"""
+
+import binascii
+
+class intel_hex:
+    def __init__(self, filename):
+        self.start_address = 0
+        self.address = 0
+        self.memory = {}
+        self.lineno = 0
+        file = open(filename, 'r')
+        for line in file:
+            self.lineno += 1
+            if self.parse_line(line):
+                break
+        file.close()
+        self.pack()
+
+    def pack(self):
+        memory = {}
+        prev_addr = 0
+        prev_data_len = 0
+        for addr in sorted(self.memory.keys()):
+            data = self.memory[addr]
+            if addr == prev_addr + prev_data_len:
+                memory[prev_addr] += data
+                prev_data_len += len(data)
+            else:
+                memory[addr] = data
+                prev_addr = addr
+                prev_data_len = len(data)
+       self.memory = memory
+
+    def calc_checksum(self, byte_count, offset, type_code, data):
+        s = byte_count
+        s += (offset >> 8)
+        s += offset & 0xff
+        s += type_code
+        for d in data:
+            s += (ord(d) & 0xff)
+        s &= 0xff
+        if s != 0:
+            s = 256 - s
+        return s
+
+    def add_data(self, count, offset, data):
+        address = self.address + offset
+        try:
+            self.memory[address]
+        except:
+            pass
+        else:
+            raise ValueError, "data overwritten (%d)" % self.lineno
+        self.memory[address] = data
+
+    def parse_line(self, line):
+        if line[0] != ':':
+            raise ValueError, "invalid line (%d)" % self.lineno
+        count = int(line[1:3], 16)
+        offset = int(line[3:7], 16)
+        type_code = int(line[7:9], 16)
+        data = binascii.unhexlify(line[9:(9+count*2)])
+        check_sum = int(line[(9+count*2):], 16)
+        if check_sum != self.calc_checksum(count, offset, type_code, data):
+            raise ValueError, "invalid checksum (%d)" % self.lineno
+        if type_code == 0x00:
+            self.add_data(count, offset, data)
+            return 0
+        elif type_code == 0x01:
+            return 1
+        elif type_code == 0x04:
+            if count != 2:
+                raise ValueError, "invalid count (%d): (%d) Expected 2" \
+                    % (self.lineno, count)
+            self.address = ((ord(data[0])&0xff)<<24) + ((ord(data[1])&0xff)<<16)
+            return 0
+        elif type_code == 0x05:
+            if count != 4:
+                raise ValueError, "invalid count (%d): (%d) Expected 4" \
+                    % (self.lineno, count)
+            self.start_address \
+                = ((ord(data[0])&0xff)<<24) + ((ord(data[1])&0xff)<<16) \
+                + ((ord(data[2])&0xff)<<8) + ((ord(data[3])&0xff))
+            return 0
+        else:
+            raise ValueError, "invalid type code (%d): (%d)" \
+                % (self.lineno, type_code)
diff --git a/tool/openocd-script/lock.tcl b/tool/openocd-script/lock.tcl
new file mode 100644 (file)
index 0000000..ecb5a96
--- /dev/null
@@ -0,0 +1,6 @@
+init
+reset
+halt
+stm32x lock 0
+reset
+exit
diff --git a/tool/openocd-script/options_read.tcl b/tool/openocd-script/options_read.tcl
new file mode 100644 (file)
index 0000000..7ea8c40
--- /dev/null
@@ -0,0 +1,5 @@
+init
+reset
+halt
+stm32x options_read 0
+exit
diff --git a/tool/openocd-script/unlock.tcl b/tool/openocd-script/unlock.tcl
new file mode 100644 (file)
index 0000000..2f14aeb
--- /dev/null
@@ -0,0 +1,6 @@
+init
+reset
+halt
+stm32x unlock 0
+reset
+exit
diff --git a/tool/openocd-script/write.tcl b/tool/openocd-script/write.tcl
new file mode 100644 (file)
index 0000000..a99747c
--- /dev/null
@@ -0,0 +1,5 @@
+init
+reset
+halt
+flash write_image erase neug.elf
+exit