Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 25 additions & 25 deletions dfu-convert
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
#!/usr/bin/env python2
#!/usr/bin/env python

# Written by Antonio Galea - 2010/11/18
# Distributed under Gnu LGPL 3.0
# see http://www.gnu.org/licenses/lgpl-3.0.txt

import sys,struct,zlib,os
from optparse import OptionParser
from intelhex import IntelHex

DEFAULT_DEVICE="0x0483:0xdf11"

Expand All @@ -16,59 +15,59 @@ def consume(fmt,data,names):
n = struct.calcsize(fmt)
return named(struct.unpack(fmt,data[:n]),names),data[n:]
def cstring(string):
return string.split('\0',1)[0]
return string.split(b'\0',1)[0]
def compute_crc(data):
return 0xFFFFFFFF & -zlib.crc32(data) -1

def parse(file,dump_images=False):
print 'File: "%s"' % file
print('File: "%s"' % file)
data = open(file,'rb').read()
crc = compute_crc(data[:-4])
prefix, data = consume('<5sBIB',data,'signature version size targets')
print '%(signature)s v%(version)d, image size: %(size)d, targets: %(targets)d' % prefix
print('%(signature)s v%(version)d, image size: %(size)d, targets: %(targets)d' % prefix)
for t in range(prefix['targets']):
tprefix, data = consume('<6sBI255s2I',data,'signature altsetting named name size elements')
tprefix['num'] = t
if tprefix['named']:
tprefix['name'] = cstring(tprefix['name'])
else:
tprefix['name'] = ''
print '%(signature)s %(num)d, alt setting: %(altsetting)s, name: "%(name)s", size: %(size)d, elements: %(elements)d' % tprefix
print('%(signature)s %(num)d, alt setting: %(altsetting)s, name: "%(name)s", size: %(size)d, elements: %(elements)d' % tprefix)
tsize = tprefix['size']
target, data = data[:tsize], data[tsize:]
for e in range(tprefix['elements']):
eprefix, target = consume('<2I',target,'address size')
eprefix['num'] = e
print ' %(num)d, address: 0x%(address)08x, size: %(size)d' % eprefix
print(' %(num)d, address: 0x%(address)08x, size: %(size)d' % eprefix)
esize = eprefix['size']
image, target = target[:esize], target[esize:]
if dump_images:
out = '%s.target%d.image%d.bin' % (file,t,e)
open(out,'wb').write(image)
print ' DUMPED IMAGE TO "%s"' % out
print(' DUMPED IMAGE TO "%s"' % out)
if len(target):
print "target %d: PARSE ERROR" % t
print("target %d: PARSE ERROR" % t)
suffix = named(struct.unpack('<4H3sBI',data[:16]),'device product vendor dfu ufd len crc')
print 'usb: %(vendor)04x:%(product)04x, device: 0x%(device)04x, dfu: 0x%(dfu)04x, %(ufd)s, %(len)d, 0x%(crc)08x' % suffix
print('usb: %(vendor)04x:%(product)04x, device: 0x%(device)04x, dfu: 0x%(dfu)04x, %(ufd)s, %(len)d, 0x%(crc)08x' % suffix)
if crc != suffix['crc']:
print "CRC ERROR: computed crc32 is 0x%08x" % crc
print("CRC ERROR: computed crc32 is 0x%08x" % crc)
data = data[16:]
if data:
print "PARSE ERROR"
print("PARSE ERROR")

def build(file,targets,device=DEFAULT_DEVICE):
data = ''
data = b''
for t,target in enumerate(targets):
tdata = ''
tdata = b''
for image in target:
tdata += struct.pack('<2I',image['address'],len(image['data']))+image['data']
tdata = struct.pack('<6sBI255s2I','Target',0,1,'ST...',len(tdata),len(target)) + tdata
tdata += struct.pack(b'<2I',image['address'],len(image['data']))+image['data']
tdata = struct.pack(b'<6sBI255s2I',b'Target',0,1,b'ST...',len(tdata),len(target)) + tdata
data += tdata
data = struct.pack('<5sBIB','DfuSe',1,len(data)+11,len(targets)) + data
data = struct.pack(b'<5sBIB',b'DfuSe',1,len(data)+11,len(targets)) + data
v,d=map(lambda x: int(x,0) & 0xFFFF, device.split(':',1))
data += struct.pack('<4H3sB',0,d,v,0x011a,'UFD',16)
data += struct.pack(b'<4H3sB',0,d,v,0x011a,b'UFD',16)
crc = compute_crc(data)
data += struct.pack('<I',crc)
data += struct.pack(b'<I',crc)
open(file,'wb').write(data)

if __name__=="__main__":
Expand Down Expand Up @@ -96,27 +95,28 @@ if __name__=="__main__":
try:
address,binfile = arg.split(':',1)
except ValueError:
print "Address:file couple '%s' invalid." % arg
print("Address:file couple '%s' invalid." % arg)
sys.exit(1)
try:
address = int(address,0) & 0xFFFFFFFF
except ValueError:
print "Address %s invalid." % address
print("Address %s invalid." % address)
sys.exit(1)
if not os.path.isfile(binfile):
print "Unreadable file '%s'." % binfile
print("Unreadable file '%s'." % binfile)
sys.exit(1)
target.append({ 'address': address, 'data': open(binfile,'rb').read() })

if options.hexfiles:
for hex in options.hexfiles:
from intelhex import IntelHex
ih = IntelHex(hex)
address = ih.minaddr()
data = ih.tobinstr()
try:
address = address & 0xFFFFFFFF
except ValueError:
print "Address %s invalid." % address
print("Address %s invalid." % address)
sys.exit(1)
target.append({ 'address': address, 'data': data })

Expand All @@ -127,13 +127,13 @@ if __name__=="__main__":
try:
v,d=map(lambda x: int(x,0) & 0xFFFF, device.split(':',1))
except:
print "Invalid device '%s'." % device
print("Invalid device '%s'." % device)
sys.exit(1)
build(outfile,[target],device)
elif len(args)==1:
infile = args[0]
if not os.path.isfile(infile):
print "Unreadable file '%s'." % infile
print("Unreadable file '%s'." % infile)
sys.exit(1)
parse(infile, dump_images=options.dump_images)
else:
Expand Down
184 changes: 144 additions & 40 deletions dfuse-tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ def list_dfu(args):
if usbdev is None:
raise ValueError('No STM32 DfuSe device found.')

dfu = dfuse.DfuDevice(usbdev)
dfu = find_device(args) #dfuse.DfuDevice(usbdev)
for name, alt in dfu.alternates():
print ("Device: [%.4x:%.4x] Cfg: %d Intf: %d Alt: %d '%s'" % ( \
alt.device.idVendor, \
Expand All @@ -37,30 +37,63 @@ def list_dfu(args):
alt.bInterfaceNumber, \
alt.bAlternateSetting, \
name))
mem = dfu.get_mem_layout()
print(" memory with %d pages %d bytes, start address %.8x"
% (mem['pageno'], mem['pagesize'], mem['address']
))
states = ["Readable" if mem['readable'] else "",
"Writable" if mem['writable'] else "",
"Erasable" if mem['erasable'] else ""]
print(" device state: %s\n" % ", ".join(states))

def leave_dfu(args):
dfu = find_device(args)
dfu.leave()
status = dfu.get_status()
if status[0] > 0:
raise RuntimeError("An error occured. Status: %r" % status)
raise RuntimeError("An error occured. Status: %r %r" %
(status[1], dfuse.DfuState.string(status[1])))

def erase(args):
dfu = find_device(args)
mem = dfu.get_mem_layout()
if mem is None:
end = int(args.erase[1], 0) if len(args.erase) > 1 else 1024
pagesize = 1024
elif not mem['erasable']:
print("Device not erasable, exits now!")
return
else:
end = (int(args.erase[1], 0) if len(args.erase) > 1 else
mem['address'] + mem['pagesize'] * mem['pageno'])
pagesize = mem['pagesize']

print ("Erasing. Please wait this might be long ...")
dfu.erase(args.erase)
status = dfu.wait_while_state(dfuse.DfuState.DFU_DOWNLOAD_BUSY)

if status[1] != dfuse.DfuState.DFU_DOWNLOAD_IDLE:
raise RuntimeError("An error occured. Device Status: %r" % status)
addr = int(args.erase[0], 0) if len(args.erase) else mem['address']
cnt = 0
while addr < end:
dfu.erase(addr)
# must send after erase command page page 16 AN3156
dfu.wait_while_state(dfuse.DfuState.DFU_DOWNLOAD_BUSY)
print("Erasing page starting at %.8x" % addr)
addr += pagesize

if dfu.get_status()[1] != dfuse.DfuState.DFU_DOWNLOAD_IDLE:
raise RuntimeError("An error occured. Status: %r %r" %
(status[1], dfuse.DfuState.string(status[1])))
dfu.clear_status()

print ("Done !")

def flash(args):
dfufile = args.flash[0]
dfu = find_device(args)

mem = dfu.get_mem_layout()
if mem is not None and not mem['writable']:
print("Device not writable, exits now!")
return

if (dfufile.devInfo['vid'] != dfu.dev.idVendor or dfufile.devInfo['pid'] != dfu.dev.idProduct) and not args.force:
raise ValueError("Vendor/Product id mismatch: [%.4x:%.4x] (file) [%.4x:%.4x] (device). Trying running with --force" % ( \
dfufile.devInfo['vid'], \
Expand All @@ -84,43 +117,109 @@ def flash(args):
dfu.erase(image['address'])
status = dfu.wait_while_state(dfuse.DfuState.DFU_DOWNLOAD_BUSY)
if status[1] != dfuse.DfuState.DFU_DOWNLOAD_IDLE:
raise RuntimeError("An error occured. Device Status: %r" % status)
raise RuntimeError("An error occured. Status: %r %r" %
(status[1], dfuse.DfuState.string(status[1])))

print("Flashing ...")
transfer_size = 1024
dfu.set_address(image['address'])
status = dfu.wait_while_state(dfuse.DfuState.DFU_DOWNLOAD_BUSY)
if status[1] != dfuse.DfuState.DFU_DOWNLOAD_IDLE:
raise RuntimeError("An error occured. Device Status: %r" % status)

data = image['data']
blocks = [data[i:i + transfer_size] for i in range(0, len(data), transfer_size)]
for blocknum, block in enumerate(blocks):
print("Flashing block %r" % blocknum)
dfu.write(blocknum, block)
status = dfu.wait_while_state(dfuse.DfuState.DFU_DOWNLOAD_BUSY)
if status[1] != dfuse.DfuState.DFU_DOWNLOAD_IDLE:
raise RuntimeError("An error occured. Device Status: %r" % status)
transfer_size = mem['pagesize'] if mem else 1024
_flash(image['address'], transfer_size, image['data'], dfu)

print("Done")

def flash_bin(args):
dfu = find_device(args)
mem = dfu.get_mem_layout()
if mem is None:
raise RuntimeError("USB interface description could not be parsed.")
if not mem['writable']:
raise RuntimeError("Device not writable.")

with open(args.flash_bin[0],'rb') as f:
data = bytearray(f.read())
if not data:
raise ValueError("File %r could not be read" % args.flash_bin[0])

print("Flashing ...")
transfer_size = mem['pagesize'] if mem else 1024
_flash(mem['address'], transfer_size, data, dfu)

return
print("Done !")

def _flash(address, transfer_size, data, dfu):
dfu.set_address(address)
status = dfu.wait_while_state(dfuse.DfuState.DFU_DOWNLOAD_BUSY)

if status[1] != dfuse.DfuState.DFU_IDLE and status[1] != dfuse.DfuState.DFU_DOWNLOAD_IDLE:
raise RuntimeError("An error occured. Status: %r" % status)
if status[1] != dfuse.DfuState.DFU_DOWNLOAD_IDLE:
raise RuntimeError("An error occured. Status: %r %r" %
(status[1], dfuse.DfuState.string(status[1])))

print ("Done !")
blocks = [data[i:i + transfer_size] for i in range(0, len(data), transfer_size)]
for blocknum, block in enumerate(blocks):
print("Flashing block %r %rbytes" % (blocknum, (blocknum + 1) * transfer_size))
dfu.write(blocknum, block)
status = dfu.wait_while_state(dfuse.DfuState.DFU_DOWNLOAD_BUSY)
if status[1] != dfuse.DfuState.DFU_DOWNLOAD_IDLE:
raise RuntimeError("An error occured. Status: %r %r" %
(status[1], dfuse.DfuState.string(status[1])))

dfu.clear_status()

def read(args):
dfu = find_device(args)
mem = dfu.get_mem_layout()
if mem is not None and not mem['readable']:
print("Device not readable, exits now!")
return

transactions = 0
maxbytes = mem['pagesize'] * (mem['pageno'] if len(args.read) < 2 else int(args.read[1], 0))
data = bytearray()
bytes_read = 0
bytes_to_read = 0

print("Copying data from DFU device")

while bytes_read < maxbytes:
bytes_to_read = mem['pagesize']
result = dfu.upload(transactions, bytes_to_read)
if not result:
break
elif transactions == 0:
transactions += 2
continue
elif (len(result) > 0):
data.extend(result)
bytes_read += len(result)
transactions += 1
print("Read page {0} {1} bytes".format(transactions -3, len(result)))

if len(result) != bytes_to_read:
break

status = dfu.get_status()
dfu.clear_status()
if status[1] not in [dfuse.DfuState.DFU_IDLE, dfuse.DfuState.DFU_UPLOAD_IDLE]:
filename = args.read[0]+'.error'
with open(filename,'wb') as f:
f.write(bytearray(data))
raise RuntimeError("An error occured. Status: %r %r, saved retrieved data to file %r" %
(status[1], dfuse.DfuState.string(status[1]), filename))

print('Done, read {0} bytes'.format(bytes_read))

with open(args.read[0],'wb') as f:
f.write(bytearray(data))

parser = argparse.ArgumentParser(description="DfuSe flashing util for STM32")

action = parser.add_mutually_exclusive_group(required = True)
action.add_argument('--list', action='store_true', help='List available DfuSe interfaces')
action.add_argument('--leave', action='store_true', help='Leave DFU mode')
action.add_argument('--flash', nargs=1, action='store', help='Flash a DfuSe file', metavar='FILE', type=dfuse.DfuFile)
action.add_argument('--erase', action='store', help='Erase page at ADDRESS (must be page aligned)', metavar=('ADDRESS'), type=int)
action.add_argument('--flash-bin', nargs=1, action='store', help='Flash a ordinary bin file', metavar='FILE', type=str)
action.add_argument('--read', nargs='+', action='store', help='Read device memory', metavar=('SAVEFILE', 'PAGES'), type=str)
action.add_argument('--erase', nargs='*', action='store',
help='Erase all or from ADDRESS to page ENDADDR or end of memory (must be page aligned)',
metavar=('ADDRESS', 'ENDADDR'))

devinfo = parser.add_argument_group('Device information')
devinfo.add_argument('--vid', action='store', type=int, default=0x0483, help='Device\'s USB vendor id, defaults to 0x0483')
Expand All @@ -134,15 +233,20 @@ def flash(args):

args = parser.parse_args()

#try:
if args.list:
list_dfu(args)
elif args.leave:
leave_dfu(args)
elif args.erase is not None:
erase(args)
elif args.flash is not None:
flash(args)
#except Exception as e:
# print(e, file=sys.stderr)
# sys.exit(-1)
try:
#if 1:
if args.list:
list_dfu(args)
elif args.leave:
leave_dfu(args)
elif args.erase is not None:
erase(args)
elif args.flash is not None:
flash(args)
elif args.flash_bin is not None:
flash_bin(args)
elif args.read is not None:
read(args)
except Exception as e:
print(e, file=sys.stderr)
sys.exit(-1)
Loading