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 import sys
,struct
,zlib
,os
8 from optparse
import OptionParser
9 from intelhex
import IntelHex
11 DEFAULT_DEVICE
="0x0483:0xdf11"
13 def named(tuple,names
):
14 return dict(zip(names
.split(),tuple))
15 def consume(fmt
,data
,names
):
16 n
= struct
.calcsize(fmt
)
17 return named(struct
.unpack(fmt
,data
[:n
]),names
),data
[n
:]
19 return string
.split('\0',1)[0]
20 def compute_crc(data
):
21 return 0xFFFFFFFF & -zlib
.crc32(data
) -1
23 def parse(file,dump_images
=False):
24 print 'File: "%s"' % file
25 data
= open(file,'rb').read()
26 crc
= compute_crc(data
[:-4])
27 prefix
, data
= consume('<5sBIB',data
,'signature version size targets')
28 print '%(signature)s v%(version)d, image size: %(size)d, targets: %(targets)d' % prefix
29 for t
in range(prefix
['targets']):
30 tprefix
, data
= consume('<6sBI255s2I',data
,'signature altsetting named name size elements')
33 tprefix
['name'] = cstring(tprefix
['name'])
36 print '%(signature)s %(num)d, alt setting: %(altsetting)s, name: "%(name)s", size: %(size)d, elements: %(elements)d' % tprefix
37 tsize
= tprefix
['size']
38 target
, data
= data
[:tsize
], data
[tsize
:]
39 for e
in range(tprefix
['elements']):
40 eprefix
, target
= consume('<2I',target
,'address size')
42 print ' %(num)d, address: 0x%(address)08x, size: %(size)d' % eprefix
43 esize
= eprefix
['size']
44 image
, target
= target
[:esize
], target
[esize
:]
46 out
= '%s.target%d.image%d.bin' % (file,t
,e
)
47 open(out
,'wb').write(image
)
48 print ' DUMPED IMAGE TO "%s"' % out
50 print "target %d: PARSE ERROR" % t
51 suffix
= named(struct
.unpack('<4H3sBI',data
[:16]),'device product vendor dfu ufd len crc')
52 print 'usb: %(vendor)04x:%(product)04x, device: 0x%(device)04x, dfu: 0x%(dfu)04x, %(ufd)s, %(len)d, 0x%(crc)08x' % suffix
53 if crc
!= suffix
['crc']:
54 print "CRC ERROR: computed crc32 is 0x%08x" % crc
59 def build(file,targets
,device
=DEFAULT_DEVICE
):
61 for t
,target
in enumerate(targets
):
64 tdata
+= struct
.pack('<2I',image
['address'],len(image
['data']))+image
['data']
65 tdata
= struct
.pack('<6sBI255s2I','Target',0,1,'ST...',len(tdata
),len(target
)) + tdata
67 data
= struct
.pack('<5sBIB','DfuSe',1,len(data
)+11,len(targets
)) + data
68 v
,d
=map(lambda x
: int(x
,0) & 0xFFFF, device
.split(':',1))
69 data
+= struct
.pack('<4H3sB',0,d
,v
,0x011a,'UFD',16)
70 crc
= compute_crc(data
)
71 data
+= struct
.pack('<I',crc
)
72 open(file,'wb').write(data
)
74 if __name__
=="__main__":
76 %prog [-d|--dump] infile.dfu
77 %prog {-b|--build} address:file.bin [-b address:file.bin ...] [{-D|--device}=vendor:device] outfile.dfu
78 %prog {-i|--ihex} file.hex [-i file.hex ...] [{-D|--device}=vendor:device] outfile.dfu"""
80 parser
= OptionParser(usage
=usage
)
81 parser
.add_option("-b", "--build", action
="append", dest
="binfiles",
82 help="build a DFU file from given BINFILES", metavar
="BINFILES")
83 parser
.add_option("-i", "--ihex", action
="append", dest
="hexfiles",
84 help="build a DFU file from given HEXFILES", metavar
="HEXFILES")
85 parser
.add_option("-D", "--device", action
="store", dest
="device",
86 help="build for DEVICE, defaults to %s" % DEFAULT_DEVICE
, metavar
="DEVICE")
87 parser
.add_option("-d", "--dump", action
="store_true", dest
="dump_images",
88 default
=False, help="dump contained images to current directory")
89 (options
, args
) = parser
.parse_args()
91 if (options
.binfiles
or options
.hexfiles
) and len(args
)==1:
95 for arg
in options
.binfiles
:
97 address
,binfile
= arg
.split(':',1)
99 print "Address:file couple '%s' invalid." % arg
102 address
= int(address
,0) & 0xFFFFFFFF
104 print "Address %s invalid." % address
106 if not os
.path
.isfile(binfile
):
107 print "Unreadable file '%s'." % binfile
109 target
.append({ 'address': address
, 'data': open(binfile
,'rb').read() })
112 for hex in options
.hexfiles
:
114 address
= ih
.minaddr()
117 address
= address
& 0xFFFFFFFF
119 print "Address %s invalid." % address
121 target
.append({ 'address': address
, 'data': data
})
124 device
= DEFAULT_DEVICE
126 device
=options
.device
128 v
,d
=map(lambda x
: int(x
,0) & 0xFFFF, device
.split(':',1))
130 print "Invalid device '%s'." % device
132 build(outfile
,[target
],device
)
135 if not os
.path
.isfile(infile
):
136 print "Unreadable file '%s'." % infile
138 parse(infile
, dump_images
=options
.dump_images
)