From bdcff41b7cc0fbdd9c778686059004ac097fada9 Mon Sep 17 00:00:00 2001 From: NIIBE Yutaka Date: Mon, 1 Nov 2010 18:14:10 +0900 Subject: [PATCH] DfuSe writer. --- ChangeLog | 5 + tool/dfu_stmicroelectronics_extention.py | 243 +++++++++++++++++++++++ tool/intel_hex.py | 85 ++++++++ 3 files changed, 333 insertions(+) create mode 100755 tool/dfu_stmicroelectronics_extention.py create mode 100644 tool/intel_hex.py diff --git a/ChangeLog b/ChangeLog index 920f8c1..5f78e3d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2010-11-01 NIIBE Yutaka + + * tool/intel_hex.py: New file. + * tool/dfu_stmicroelectronics_extention.py: New file. + 2010-10-28 NIIBE Yutaka * src/gnuk.h (OPENPGP_CARD_INITIAL_PW3): New. diff --git a/tool/dfu_stmicroelectronics_extention.py b/tool/dfu_stmicroelectronics_extention.py new file mode 100755 index 0000000..f43aefa --- /dev/null +++ b/tool/dfu_stmicroelectronics_extention.py @@ -0,0 +1,243 @@ +#! /usr/bin/python + +from intel_hex import * +import sys, time, struct + +""" +DFU tool for STM32 Processor. +""" + +# INPUT: intel hex file + +# Seems that following is not supported current 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 = 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: printer usb.Device object. + configuration: configuration number. + interface: printer usb.Interface object representing the interface and altenate setting. + """ + if interface.interfaceClass != DFU_CLASS: + raise TypeError, "Wrong interface class" + if interface.interfaceSubClass != DFU_SUBCLASS: + raise TypeError, "Wrong interface sub class" + + 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 __del__(self): + try: + self.__devhandle.releaseInterface(self.__intf) + del self.__devhandle + except: + pass + + 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) + + 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_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): + data_block = [(ord(b) & 0xff) for b in block] + blocknum = self.__blocknum + self.__blocknum = self.__blocknum + 1 + self.ll_download_block(blocknum, data_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, filename): + ih = intel_hex(filename) + # 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 + while addr < end_addr: + self.dfuse_erase(addr) + sys.stdout.write("#") + sys.stdout.flush() + addr += 1024 + 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 + # + 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]) + sys.stdout.write("#") + sys.stdout.flush() + addr += 1024 + i += 1 + sys.stdout.write("\n") + sys.stdout.flush() + +busses = usb.busses() + +def get_device(): + for bus in busses: + devices = bus.devices + for dev in devices: + 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: + 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() + if s[4] == STATE_DFU_IDLE: + print s + exit + transfer_size = 1024 + if s[0] != DFU_STATUS_OK: + print s + exit + dfu.download(filename) + +if __name__ == '__main__': + main(sys.argv[1]) diff --git a/tool/intel_hex.py b/tool/intel_hex.py new file mode 100644 index 0000000..b397b5c --- /dev/null +++ b/tool/intel_hex.py @@ -0,0 +1,85 @@ +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)