DfuSe writer.
This commit is contained in:
@@ -1,3 +1,8 @@
|
|||||||
|
2010-11-01 NIIBE Yutaka <gniibe@fsij.org>
|
||||||
|
|
||||||
|
* tool/intel_hex.py: New file.
|
||||||
|
* tool/dfu_stmicroelectronics_extention.py: New file.
|
||||||
|
|
||||||
2010-10-28 NIIBE Yutaka <gniibe@fsij.org>
|
2010-10-28 NIIBE Yutaka <gniibe@fsij.org>
|
||||||
|
|
||||||
* src/gnuk.h (OPENPGP_CARD_INITIAL_PW3): New.
|
* src/gnuk.h (OPENPGP_CARD_INITIAL_PW3): New.
|
||||||
|
|||||||
243
tool/dfu_stmicroelectronics_extention.py
Executable file
243
tool/dfu_stmicroelectronics_extention.py
Executable file
@@ -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])
|
||||||
85
tool/intel_hex.py
Normal file
85
tool/intel_hex.py
Normal file
@@ -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)
|
||||||
Reference in New Issue
Block a user