dfu.py 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  1. #!/usr/bin/python
  2. # Written by Antonio Galea - 2010/11/18
  3. # Distributed under Gnu LGPL 3.0
  4. # see http://www.gnu.org/licenses/lgpl-3.0.txt
  5. import sys,struct,zlib,os
  6. from optparse import OptionParser
  7. DEFAULT_DEVICE="0x0483:0xdf11"
  8. def named(tuple,names):
  9. return dict(zip(names.split(),tuple))
  10. def consume(fmt,data,names):
  11. n = struct.calcsize(fmt)
  12. return named(struct.unpack(fmt,data[:n]),names),data[n:]
  13. def cstring(string):
  14. return string.split('\0',1)[0]
  15. def compute_crc(data):
  16. return 0xFFFFFFFF & -zlib.crc32(data) -1
  17. def parse(file,dump_images=False):
  18. print 'File: "%s"' % file
  19. data = open(file,'rb').read()
  20. crc = compute_crc(data[:-4])
  21. prefix, data = consume('<5sBIB',data,'signature version size targets')
  22. print '%(signature)s v%(version)d, image size: %(size)d, targets: %(targets)d' % prefix
  23. for t in range(prefix['targets']):
  24. tprefix, data = consume('<6sBI255s2I',data,'signature altsetting named name size elements')
  25. tprefix['num'] = t
  26. if tprefix['named']:
  27. tprefix['name'] = cstring(tprefix['name'])
  28. else:
  29. tprefix['name'] = ''
  30. print '%(signature)s %(num)d, alt setting: %(altsetting)s, name: "%(name)s", size: %(size)d, elements: %(elements)d' % tprefix
  31. tsize = tprefix['size']
  32. target, data = data[:tsize], data[tsize:]
  33. for e in range(tprefix['elements']):
  34. eprefix, target = consume('<2I',target,'address size')
  35. eprefix['num'] = e
  36. print ' %(num)d, address: 0x%(address)08x, size: %(size)d' % eprefix
  37. esize = eprefix['size']
  38. image, target = target[:esize], target[esize:]
  39. if dump_images:
  40. out = '%s.target%d.image%d.bin' % (file,t,e)
  41. open(out,'wb').write(image)
  42. print ' DUMPED IMAGE TO "%s"' % out
  43. if len(target):
  44. print "target %d: PARSE ERROR" % t
  45. suffix = named(struct.unpack('<4H3sBI',data[:16]),'device product vendor dfu ufd len crc')
  46. print 'usb: %(vendor)04x:%(product)04x, device: 0x%(device)04x, dfu: 0x%(dfu)04x, %(ufd)s, %(len)d, 0x%(crc)08x' % suffix
  47. if crc != suffix['crc']:
  48. print "CRC ERROR: computed crc32 is 0x%08x" % crc
  49. data = data[16:]
  50. if data:
  51. print "PARSE ERROR"
  52. def build(file,targets,device=DEFAULT_DEVICE):
  53. data = ''
  54. for t,target in enumerate(targets):
  55. tdata = ''
  56. for image in target:
  57. tdata += struct.pack('<2I',image['address'],len(image['data']))+image['data']
  58. tdata = struct.pack('<6sBI255s2I','Target',0,1,'ST...',len(tdata),len(target)) + tdata
  59. data += tdata
  60. data = struct.pack('<5sBIB','DfuSe',1,len(data)+11,len(targets)) + data
  61. v,d=map(lambda x: int(x,0) & 0xFFFF, device.split(':',1))
  62. data += struct.pack('<4H3sB',0,d,v,0x011a,'UFD',16)
  63. crc = compute_crc(data)
  64. data += struct.pack('<I',crc)
  65. open(file,'wb').write(data)
  66. if __name__=="__main__":
  67. usage = """
  68. %prog [-d|--dump] infile.dfu
  69. %prog {-b|--build} address:file.bin [-b address:file.bin ...] [{-D|--device}=vendor:device] outfile.dfu"""
  70. parser = OptionParser(usage=usage)
  71. parser.add_option("-b", "--build", action="append", dest="binfiles",
  72. help="build a DFU file from given BINFILES", metavar="BINFILES")
  73. parser.add_option("-D", "--device", action="store", dest="device",
  74. help="build for DEVICE, defaults to %s" % DEFAULT_DEVICE, metavar="DEVICE")
  75. parser.add_option("-d", "--dump", action="store_true", dest="dump_images",
  76. default=False, help="dump contained images to current directory")
  77. (options, args) = parser.parse_args()
  78. if options.binfiles and len(args)==1:
  79. target = []
  80. for arg in options.binfiles:
  81. try:
  82. address,binfile = arg.split(':',1)
  83. except ValueError:
  84. print "Address:file couple '%s' invalid." % arg
  85. sys.exit(1)
  86. try:
  87. address = int(address,0) & 0xFFFFFFFF
  88. except ValueError:
  89. print "Address %s invalid." % address
  90. sys.exit(1)
  91. if not os.path.isfile(binfile):
  92. print "Unreadable file '%s'." % binfile
  93. sys.exit(1)
  94. target.append({ 'address': address, 'data': open(binfile,'rb').read() })
  95. outfile = args[0]
  96. device = DEFAULT_DEVICE
  97. if options.device:
  98. device=options.device
  99. try:
  100. v,d=map(lambda x: int(x,0) & 0xFFFF, device.split(':',1))
  101. except:
  102. print "Invalid device '%s'." % device
  103. sys.exit(1)
  104. build(outfile,[target],device)
  105. elif len(args)==1:
  106. infile = args[0]
  107. if not os.path.isfile(infile):
  108. print "Unreadable file '%s'." % infile
  109. sys.exit(1)
  110. parse(infile, dump_images=options.dump_images)
  111. else:
  112. parser.print_help()
  113. sys.exit(1)