Skip to content

Commit 3af035a

Browse files
committed
Rework upload support.
Remove unneeded complexity to support streams, as firmware will hardly be a memory concern.
1 parent 6879d87 commit 3af035a

File tree

2 files changed

+102
-56
lines changed

2 files changed

+102
-56
lines changed

dfu.py

+59-56
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
from cStringIO import StringIO
21
from struct import pack, unpack, calcsize
32
from zlib import crc32
43
from time import sleep
@@ -488,13 +487,24 @@ def download(self, firmware, blocknum=None):
488487
sleep(timeout / 1000)
489488
return blocknum
490489

491-
def upload(self, blocknum, length):
490+
def upload(self, length, blocknum=None):
491+
blocknum = self.getNextBlockNumber(blocknum)
492492
if self.__has_stm_extensions and blocknum < 2:
493493
raise ValueError('upload must not be called with blocknum < 2 '
494494
'on devices supporting STM extensions')
495-
result = self._controlRead(DFU_UPLOAD, blocknum, length)
496-
self.checkStatus()
497-
return result
495+
try:
496+
result = self._controlRead(DFU_UPLOAD, blocknum, length)
497+
self.checkStatus()
498+
except Exception:
499+
exc_info = sys.exc_info()
500+
try:
501+
self.abort()
502+
except Exception:
503+
raise DoubleException(
504+
''.join(traceback.format_exception(*exc_info)),
505+
traceback.format_exc())
506+
raise exc_info[0], exc_info[1], exc_info[2]
507+
return result, blocknum
498508

499509
def getStatus(self):
500510
status, timeout, timeout_upper, state, status_descriptor = unpack(
@@ -582,6 +592,9 @@ def _parseFieldList(data, field_list):
582592
if not data:
583593
break
584594
return result
595+
def _generateFieldList(data_dict, field_list):
596+
return pack(''.join(x[1] for x in field_list),
597+
*[data_dict[x[0]] for x in field_list])
585598

586599
class DFU(object):
587600
def __init__(self, handle):
@@ -672,12 +685,8 @@ def _download(self, data):
672685
blocknum = download(data[:transfer_size], blocknum)
673686
data = data[transfer_size:]
674687

675-
def upload(self):
676-
data = StringIO()
677-
self.uploadStream(data)
678-
return data.getvalue()
679-
680-
def uploadStream(self, stream):
688+
def upload(self, vendor_specific=True, product_specific=True,
689+
version=0xffff, stm_format=False):
681690
iface = self.__dfu_interface
682691
if not iface.canUpload():
683692
raise DFUUnsupportedError('Cannot upload')
@@ -688,54 +697,48 @@ def uploadStream(self, stream):
688697
state = iface.getState()
689698
if state != DFU_STATE_DFU_IDLE:
690699
raise DFUBadSate(state)
691-
write = stream.write
700+
result = ''
692701
iface_upload = iface.upload
693702
transfer_size = iface.getTransferSize()
694703
checkStatus = iface.checkStatus
695704
abort = iface.abort
696-
if iface.hasSTExtensions():
697-
def upload():
698-
# TODO: create a valid DfuSe file.
699-
getNextBlockNumber = iface.getNextSTBlockNumber
700-
setAddress = iface.STM_setAddress
701-
for chunk in iface.STM_getDeviceMappingList():
702-
setAddress(chunk['address'])
703-
import pdb; pdb.set_trace()
704-
blocknum = None
705-
for sector in chunk['sectors']:
706-
if not sector['mode'] & DFU_ST_SECTOR_MODE_R:
707-
continue
708-
remain = sector['count'] * sector['size']
709-
while remain:
710-
blocknum = getNextBlockNumber(blocknum)
711-
data = iface_upload(blocknum, min(transfer_size,
712-
remain))
713-
checkStatus()
714-
write(data)
715-
remain -= len(data)
716-
assert remain >= 0, remain
717-
abort()
718-
else:
719-
def upload():
720-
getNextBlockNumber = iface.getNextStandardBlockNumber
705+
if stm_format and iface.hasSTMExtensions():
706+
# Untested/unfinished code ahead, raise.
707+
raise NotImplementedError
708+
# TODO: create a valid DfuSe file.
709+
setAddress = iface.STM_setAddress
710+
for chunk in iface.STM_getDeviceMappingList():
711+
setAddress(chunk['address'])
721712
blocknum = None
722-
while True:
723-
blocknum = getNextBlockNumber(blocknum)
724-
data = iface_upload(blocknum, transfer_size)
725-
checkStatus()
726-
write(data)
727-
if len(data) < transfer_size:
728-
break
729-
abort()
730-
try:
731-
upload()
732-
except:
733-
exc_info = sys.exc_info()
734-
try:
735-
abort()
736-
except:
737-
raise DoubleException(
738-
''.join(traceback.format_exception(*exc_info)),
739-
traceback.format_exc())
740-
raise exc_info[0], exc_info[1], exc_info[2]
713+
for sector in chunk['sectors']:
714+
if not sector['mode'] & DFU_ST_SECTOR_MODE_R:
715+
continue
716+
remain = sector['count'] * sector['size']
717+
while len(result) < remain:
718+
data, blocknum = iface_upload(min(transfer_size,
719+
remain), blocknum)
720+
checkStatus()
721+
result += data
722+
assert remain == len(result), (remain, len(result))
723+
abort()
724+
else:
725+
blocknum = None
726+
while True:
727+
data, blocknum = iface_upload(transfer_size, blocknum)
728+
result += data
729+
if len(data) < transfer_size:
730+
break
731+
abort()
732+
# Add defuse tail
733+
device = self.__handle.getDevice()
734+
tail = _generateFieldList({
735+
'dfu_version': 0x110,
736+
'vendor': vendor_specific and device.getVendorID() or 0xffff,
737+
'product': product_specific and device.getProductID() or 0xffff,
738+
'device': version,
739+
}, DFU_SUFFIX_FIELD_LIST)
740+
data += ''.join(reversed(tail)) + 'UFD' + \
741+
chr(len(tail) + DFU_SUFFIX_BASE_LENGTH)
742+
data += pack('<I', crc32(data))
743+
return data
741744

dump_device.py

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
#!/usr/bin/env python
2+
from optparse import OptionParser
3+
import usb1
4+
import dfu
5+
import sys
6+
7+
parser = OptionParser()
8+
parser.add_option('-d', '--device', help='vendor[:product] (in hexadecimal)')
9+
parser.add_option('-s', '--address', help='bus[:devnum] (in hexadecimal)')
10+
11+
def main(args=None):
12+
(options, args) = parser.parse_args(args=args)
13+
vendor = options.device
14+
product = None
15+
if vendor is not None:
16+
if ':' in vendor:
17+
vendor, product = vendor.split(':')
18+
product = int(product, 16)
19+
vendor = int(vendor, 16)
20+
bus = options.address
21+
dev = None
22+
if bus is not None:
23+
if ':' in bus:
24+
bus, dev = address.split(':', 1)
25+
dev = int(dev, 16)
26+
bus = int(bus, 16)
27+
context = usb1.LibUSBContext()
28+
for device in context.getDeviceList():
29+
if (vendor is not None and (vendor != device.getVendorID() or \
30+
(product is not None and product != device.getProductID()))) \
31+
or (bus is not None and (bus != device.getBusNumber() or \
32+
(dev is not None and dev != device.getDeviceAddress()))):
33+
continue
34+
break
35+
else:
36+
print 'No device found.'
37+
sys.exit(1)
38+
dfu_device = dfu.DFU(device.open())
39+
print dfu_device.upload()
40+
41+
if __name__ == '__main__':
42+
main()
43+

0 commit comments

Comments
 (0)