3 # Written by Antonio Galea - 2010/11/18
4 # Distributed under Gnu LGPL 3.0
5 # see http://www.gnu.org/licenses/lgpl-3.0.txt
7 # based on a modified version of this script from https://sourceforge.net/p/dfu-util/tickets/35/#357c
8 # with the patch supplied in https://sourceforge.net/p/dfu-util/tickets/35/#a2b6
10 import sys
,struct
,zlib
,os
12 from optparse
import OptionParser
13 from intelhex
import IntelHex
15 DEFAULT_DEVICE
="0x0483:0xdf11"
18 def named(tuple,names
):
19 return dict(list(zip(names
.split(),tuple)))
20 def consume(fmt
,data
,names
):
21 n
= struct
.calcsize(fmt
)
22 return named(struct
.unpack(fmt
,data
[:n
]),names
),data
[n
:]
23 def cstring(bytestring
):
24 return bytestring
.partition(b
'\0')[0]
25 def compute_crc(data
):
26 return 0xFFFFFFFF & -zlib
.crc32(data
) -1
28 def parse(file,dump_images
=False):
29 print('File: "%s"' % file)
30 data
= open(file,'rb').read()
31 crc
= compute_crc(data
[:-4])
32 prefix
, data
= consume('<5sBIB',data
,'signature version size targets')
33 print('%(signature)s v%(version)d, image size: %(size)d, targets: %(targets)d' % prefix
)
34 for t
in range(prefix
['targets']):
35 tprefix
, data
= consume('<6sBI255s2I',data
,'signature altsetting named name size elements')
38 tprefix
['name'] = cstring(tprefix
['name'])
41 print('%(signature)s %(num)d, alt setting: %(altsetting)s, name: "%(name)s", size: %(size)d, elements: %(elements)d' % tprefix
)
42 tsize
= tprefix
['size']
43 target
, data
= data
[:tsize
], data
[tsize
:]
44 for e
in range(tprefix
['elements']):
45 eprefix
, target
= consume('<2I',target
,'address size')
47 print(' %(num)d, address: 0x%(address)08x, size: %(size)d' % eprefix
)
48 esize
= eprefix
['size']
49 image
, target
= target
[:esize
], target
[esize
:]
51 out
= '%s.target%d.image%d.bin' % (file,t
,e
)
52 open(out
,'wb').write(image
)
53 print(' DUMPED IMAGE TO "%s"' % out
)
55 print("target %d: PARSE ERROR" % t
)
56 suffix
= named(struct
.unpack('<4H3sBI',data
[:16]),'device product vendor dfu ufd len crc')
57 print('usb: %(vendor)04x:%(product)04x, device: 0x%(device)04x, dfu: 0x%(dfu)04x, %(ufd)s, %(len)d, 0x%(crc)08x' % suffix
)
58 if crc
!= suffix
['crc']:
59 print("CRC ERROR: computed crc32 is 0x%08x" % crc
)
64 def checkbin(binfile
):
65 data
= open(binfile
,'rb').read()
68 crc
= compute_crc(data
[:-4])
69 suffix
= named(struct
.unpack('<4H3sBI',data
[-16:]),'device product vendor dfu ufd len crc')
70 if crc
== suffix
['crc'] and suffix
['ufd'] == b
'UFD':
71 print('usb: %(vendor)04x:%(product)04x, device: 0x%(device)04x, dfu: 0x%(dfu)04x, %(ufd)s, %(len)d, 0x%(crc)08x' % suffix
)
72 print("It looks like the file %s has a DFU suffix!" % binfile
)
73 print("Please remove any DFU suffix and retry.")
76 def build(file,targets
,name
=DEFAULT_NAME
,device
=DEFAULT_DEVICE
):
78 for t
,target
in enumerate(targets
):
81 tdata
+= struct
.pack('<2I',image
['address'],len(image
['data']))+image
['data']
82 tdata
= struct
.pack('<6sBI255s2I',b
'Target',0,1,name
,len(tdata
),len(target
)) + tdata
84 data
= struct
.pack('<5sBIB',b
'DfuSe',1,len(data
)+11,len(targets
)) + data
85 v
,d
=[int(x
,0) & 0xFFFF for x
in device
.split(':',1)]
86 data
+= struct
.pack('<4H3sB',0x2200,d
,v
,0x011a,b
'UFD',16)
87 crc
= compute_crc(data
)
88 data
+= struct
.pack('<I',crc
)
89 open(file,'wb').write(data
)
91 if __name__
=="__main__":
93 %prog [-d|--dump] infile.dfu
94 %prog {-b|--build} address:file.bin [-b address:file.bin ...] [{-D|--device}=vendor:device] outfile.dfu
95 %prog {-s|--build-s19} file.s19 [{-D|--device}=vendor:device] outfile.dfu
96 %prog {-i|--ihex} file.hex [-i file.hex ...] [{-D|--device}=vendor:device] outfile.dfu"""
97 parser
= OptionParser(usage
=usage
)
98 parser
.add_option("-b", "--build", action
="append", dest
="binfiles",
99 help="build a DFU file from given BINFILES. Note that the BINFILES must not have any DFU suffix!", metavar
="BINFILES")
100 parser
.add_option("-i", "--ihex", action
="append", dest
="hexfiles",
101 help="build a DFU file from given HEXFILES", metavar
="HEXFILES")
102 parser
.add_option("-s", "--build-s19", type="string", dest
="s19files",
103 help="build a DFU file from given S19 S-record file.", metavar
="S19FILE")
104 parser
.add_option("-D", "--device", action
="store", dest
="device",
105 help="build for DEVICE, defaults to %s" % DEFAULT_DEVICE
, metavar
="DEVICE")
106 parser
.add_option("-d", "--dump", action
="store_true", dest
="dump_images",
107 default
=False, help="dump contained images to current directory")
108 (options
, args
) = parser
.parse_args()
110 if (options
.binfiles
or options
.hexfiles
) and len(args
)==1:
114 for arg
in options
.binfiles
:
116 address
,binfile
= arg
.split(':',1)
118 print("Address:file couple '%s' invalid." % arg
)
121 address
= int(address
,0) & 0xFFFFFFFF
123 print("Address %s invalid." % address
)
125 if not os
.path
.isfile(binfile
):
126 print("Unreadable file '%s'." % binfile
)
129 target
.append({ 'address': address
, 'data': open(binfile
,'rb').read() })
132 for hex in options
.hexfiles
:
134 for (address
,end
) in ih
.segments():
136 address
= address
& 0xFFFFFFFF
138 print("Address %s invalid." % address
)
140 target
.append({ 'address': address
, 'data': ih
.tobinstr(start
=address
, end
=end
-1)})
143 device
= DEFAULT_DEVICE
145 device
=options
.device
147 v
,d
=[int(x
,0) & 0xFFFF for x
in device
.split(':',1)]
149 print("Invalid device '%s'." % device
)
151 build(outfile
,[target
],DEFAULT_NAME
,device
)
152 elif options
.s19files
and len(args
)==1:
157 with
open(options
.s19files
) as f
:
158 lines
= f
.readlines()
163 if line
.startswith ( "S0" ):
164 name
= binascii
.a2b_hex(line
[8:len(line
) - 2]).replace(".s19", "")
165 elif line
.startswith ( "S3" ):
167 curaddress
= int(line
[4:12], 16) & 0xFFFFFFFF
169 print("Address %s invalid." % address
)
171 curdata
= binascii
.unhexlify(line
[12:-2])
172 elif line
.startswith ( "S2" ):
174 curaddress
= int(line
[4:10], 16) & 0xFFFFFFFF
176 print("Address %s invalid." % address
)
178 curdata
= binascii
.unhexlify(line
[10:-2])
179 elif line
.startswith ( "S1" ):
181 curaddress
= int(line
[4:8], 16) & 0xFFFFFFFF
183 print("Address %s invalid." % address
)
185 curdata
= binascii
.unhexlify(line
[8:-2])
189 elif address
+ len(data
) != curaddress
:
190 target
.append({ 'address': address
, 'data': data
})
196 device
= DEFAULT_DEVICE
198 device
=options
.device
200 v
,d
=[int(x
,0) & 0xFFFF for x
in device
.split(':',1)]
202 print("Invalid device '%s'." % device
)
204 build(outfile
,[target
],name
,device
)
207 if not os
.path
.isfile(infile
):
208 print("Unreadable file '%s'." % infile
)
210 parse(infile
, dump_images
=options
.dump_images
)