11#!/usr/bin/python
22
33# Written by Antonio Galea - 2010/11/18
4+ # Updated for DFU 1.1 by Sean Cross - 2020/03/31
45# Distributed under Gnu LGPL 3.0
56# see http://www.gnu.org/licenses/lgpl-3.0.txt
67
@@ -23,30 +24,7 @@ def parse(file,dump_images=False):
2324 print ('File: "%s"' % file )
2425 data = open (file ,'rb' ).read ()
2526 crc = compute_crc (data [:- 4 ])
26- prefix , data = consume ('<5sBIB' ,data ,'signature version size targets' )
27- print ('%(signature)s v%(version)d, image size: %(size)d, targets: %(targets)d' % prefix )
28- for t in range (prefix ['targets' ]):
29- tprefix , data = consume ('<6sBI255s2I' ,data ,'signature altsetting named name size elements' )
30- tprefix ['num' ] = t
31- if tprefix ['named' ]:
32- tprefix ['name' ] = cstring (tprefix ['name' ])
33- else :
34- tprefix ['name' ] = ''
35- print ('%(signature)s %(num)d, alt setting: %(altsetting)s, name: "%(name)s", size: %(size)d, elements: %(elements)d' % tprefix )
36- tsize = tprefix ['size' ]
37- target , data = data [:tsize ], data [tsize :]
38- for e in range (tprefix ['elements' ]):
39- eprefix , target = consume ('<2I' ,target ,'address size' )
40- eprefix ['num' ] = e
41- print (' %(num)d, address: 0x%(address)08x, size: %(size)d' % eprefix )
42- esize = eprefix ['size' ]
43- image , target = target [:esize ], target [esize :]
44- if dump_images :
45- out = '%s.target%d.image%d.bin' % (file ,t ,e )
46- open (out ,'wb' ).write (image )
47- print (' DUMPED IMAGE TO "%s"' % out )
48- if len (target ):
49- print ("target %d: PARSE ERROR" % t )
27+ data = data [len (data )- 16 :]
5028 suffix = named (struct .unpack ('<4H3sBI' ,data [:16 ]),'device product vendor dfu ufd len crc' )
5129 print ('usb: %(vendor)04x:%(product)04x, device: 0x%(device)04x, dfu: 0x%(dfu)04x, %(ufd)s, %(len)d, 0x%(crc)08x' % suffix )
5230 if crc != suffix ['crc' ]:
@@ -55,61 +33,57 @@ def parse(file,dump_images=False):
5533 if data :
5634 print ("PARSE ERROR" )
5735
58- def build (file ,targets ,device = DEFAULT_DEVICE ):
59- data = b''
60- for t ,target in enumerate (targets ):
61- tdata = b''
62- for image in target :
63- tdata += struct .pack ('<2I' ,image ['address' ],len (image ['data' ]))+ image ['data' ]
64- tdata = struct .pack ('<6sBI255s2I' ,b'Target' ,0 ,1 , b'ST...' ,len (tdata ),len (target )) + tdata
65- data += tdata
66- data = struct .pack ('<5sBIB' ,b'DfuSe' ,1 ,len (data )+ 11 ,len (targets )) + data
36+ def build (file ,data ,device = DEFAULT_DEVICE ):
37+ # Parse the VID and PID from the `device` argument
6738 v ,d = map (lambda x : int (x ,0 ) & 0xFFFF , device .split (':' ,1 ))
68- data += struct .pack ('<4H3sB' ,0 ,d ,v ,0x011a ,b'UFD' ,16 )
39+
40+ # Generate the DFU suffix, consisting of these fields:
41+ # Field name | Length | Description
42+ # ================+=========+================================
43+ # bcdDevice | 2 | The release number of this firmware (0xffff - don't care)
44+ # idProduct | 2 | PID of this device
45+ # idVendor | 2 | VID of this device
46+ # bcdDFU | 2 | Version of this DFU spec (0x01 0x00)
47+ # ucDfuSignature | 3 | The characters 'DFU', printed in reverse order
48+ # bLength | 1 | The length of this suffix (16 bytes)
49+ # dwCRC | 4 | A CRC32 of the data, including this suffix
50+ data += struct .pack ('<4H3sB' ,0xffff ,d ,v ,0x0100 ,b'UFD' ,16 )
6951 crc = compute_crc (data )
52+ # Append the CRC32 of the entire block
7053 data += struct .pack ('<I' ,crc )
7154 open (file ,'wb' ).write (data )
7255
7356if __name__ == "__main__" :
7457 usage = """
7558%prog [-d|--dump] infile.dfu
76- %prog {-b|--build} address: file.bin [-b address:file.bin ...] [{-D|--device}=vendor:device] outfile.dfu"""
59+ %prog {-b|--build} file.bin [{-D|--device}=vendor:device] outfile.dfu"""
7760 parser = OptionParser (usage = usage )
78- parser .add_option ("-b" , "--build" , action = "append " , dest = "binfiles " ,
79- help = "build a DFU file from given BINFILES " , metavar = "BINFILES " )
61+ parser .add_option ("-b" , "--build" , action = "store " , dest = "binfile " ,
62+ help = "build a DFU file from given BINFILE " , metavar = "BINFILE " )
8063 parser .add_option ("-D" , "--device" , action = "store" , dest = "device" ,
8164 help = "build for DEVICE, defaults to %s" % DEFAULT_DEVICE , metavar = "DEVICE" )
8265 parser .add_option ("-d" , "--dump" , action = "store_true" , dest = "dump_images" ,
8366 default = False , help = "dump contained images to current directory" )
8467 (options , args ) = parser .parse_args ()
8568
86- if options .binfiles and len (args )== 1 :
87- target = []
88- for arg in options .binfiles :
89- try :
90- address ,binfile = arg .split (':' ,1 )
91- except ValueError :
92- print ("Address:file couple '%s' invalid." % arg )
93- sys .exit (1 )
94- try :
95- address = int (address ,0 ) & 0xFFFFFFFF
96- except ValueError :
97- print ("Address %s invalid." % address )
98- sys .exit (1 )
99- if not os .path .isfile (binfile ):
100- print ("Unreadable file '%s'." % binfile )
101- sys .exit (1 )
102- target .append ({ 'address' : address , 'data' : open (binfile ,'rb' ).read () })
69+ if options .binfile and len (args )== 1 :
70+ binfile = options .binfile
71+ if not os .path .isfile (binfile ):
72+ print ("Unreadable file '%s'." % binfile )
73+ sys .exit (1 )
74+ target = open (binfile ,'rb' ).read ()
10375 outfile = args [0 ]
10476 device = DEFAULT_DEVICE
77+ # If a device is specified, parse the pair into a VID:PID pair
78+ # in order to validate them.
10579 if options .device :
10680 device = options .device
10781 try :
10882 v ,d = map (lambda x : int (x ,0 ) & 0xFFFF , device .split (':' ,1 ))
10983 except :
11084 print ("Invalid device '%s'." % device )
11185 sys .exit (1 )
112- build (outfile ,[ target ] ,device )
86+ build (outfile ,target ,device )
11387 elif len (args )== 1 :
11488 infile = args [0 ]
11589 if not os .path .isfile (infile ):
0 commit comments