From 91f36963df0db4b1b839f4afd0a7eafe5df3eef7 Mon Sep 17 00:00:00 2001 From: Samuel Sadok Date: Mon, 11 Dec 2017 10:31:15 +0100 Subject: [PATCH] add python dfuse module by plietar --- tools/dfuse/COPYING | 6 +++ tools/dfuse/DfuDevice.py | 94 ++++++++++++++++++++++++++++++++++++++++ tools/dfuse/DfuFile.py | 66 ++++++++++++++++++++++++++++ tools/dfuse/DfuState.py | 13 ++++++ tools/dfuse/DfuStatus.py | 18 ++++++++ tools/dfuse/__init__.py | 4 ++ 6 files changed, 201 insertions(+) create mode 100644 tools/dfuse/COPYING create mode 100644 tools/dfuse/DfuDevice.py create mode 100644 tools/dfuse/DfuFile.py create mode 100644 tools/dfuse/DfuState.py create mode 100644 tools/dfuse/DfuStatus.py create mode 100644 tools/dfuse/__init__.py diff --git a/tools/dfuse/COPYING b/tools/dfuse/COPYING new file mode 100644 index 00000000..37483a43 --- /dev/null +++ b/tools/dfuse/COPYING @@ -0,0 +1,6 @@ + +The Python dfuse tool was written by Paul LiƩtar. +Minor modifications were made for this project. +Original source: https://github.com/plietar/dfuse-tool + +The license for this module is unclear. diff --git a/tools/dfuse/DfuDevice.py b/tools/dfuse/DfuDevice.py new file mode 100644 index 00000000..77f40797 --- /dev/null +++ b/tools/dfuse/DfuDevice.py @@ -0,0 +1,94 @@ +import usb.util +import time + +DFU_REQUEST_SEND = 0x21 +DFU_REQUEST_RECEIVE = 0xa1 + +DFU_DETACH = 0x00 +DFU_DNLOAD = 0x01 +DFU_UPLOAD = 0x02 +DFU_GETSTATUS = 0x03 +DFU_CLRSTATUS = 0x04 +DFU_GETSTATE = 0x05 +DFU_ABORT = 0x06 + +# Order is LSB first +def address_to_4bytes(a): + return [ a % 256, (a >> 8)%256, (a >> 16)%256, (a >> 24)%256 ] + +class DfuDevice: + def __init__(self, device, timeout = None): + self.dev = device + self.timeout = timeout + self.cfg = self.dev[0] + self.intf = None + #self.dev.reset() + self.cfg.set() + + def alternates(self): + return [(usb.util.get_string(self.dev, intf.iInterface), intf) for intf in self.cfg] + + def set_alternate(self, intf): + if isinstance(intf, tuple): + self.intf = intf[1] + else: + self.intf = intf + + self.intf.set_altsetting() + + def control_msg(self, requestType, request, value, buffer, timeout=None): + return self.dev.ctrl_transfer(requestType, request, value, self.intf.bInterfaceNumber, buffer, timeout=1500) + + def detach(self, timeout): + return self.control_msg(DFU_REQUEST_SEND, DFU_DETACH, timeout, None) + + def dnload(self, blockNum, data): + cnt = self.control_msg(DFU_REQUEST_SEND, DFU_DNLOAD, blockNum, list(data)) + return cnt + + def upload(self, blockNum, size): + return self.control_msg(DFU_REQUEST_RECEIVE, DFU_UPLOAD, blockNum, size) + + def get_status(self, timeout=None): + status = self.control_msg(DFU_REQUEST_RECEIVE, DFU_GETSTATUS, 0, 6, timeout=timeout) + return (status[0], status[4], status[1] + (status[2] << 8) + (status[3] << 16), status[5]) + + def clear_status(self): + self.control_msg(DFU_REQUEST_SEND, DFU_CLRSTATUS, 0, None) + + def get_state(self): + return self.control_msg(DFU_REQUEST_RECEIVE, DFU_GETSTATE, 0, 1)[0] + + def set_address(self, ap): + return self.dnload(0x0, [0x21] + address_to_4bytes(ap)) + + def write(self, block, data): + return self.dnload(block + 2, data) + + def read(self, block, size): + return self.upload(block + 2, size) + + def erase(self, pa): + return self.dnload(0x0, [0x41] + address_to_4bytes(pa)) + + def leave(self): + return self.dnload(0x0, []) # Just send an empty data. + + def wait_while_state(self, state, timeout=None): + if not isinstance(state, (list, tuple)): + states = (state,) + else: + states = state + + status = self.get_status() + + while (status[1] in states): + timeout = status[2] + if not timeout is None: + timeout = max(timeout, status[2]) + #print("timeout = %f, claimed = %f" % (timeout, status[2])) + #time.sleep(timeout) + status = self.get_status(timeout=timeout) + + return status + diff --git a/tools/dfuse/DfuFile.py b/tools/dfuse/DfuFile.py new file mode 100644 index 00000000..175aaa27 --- /dev/null +++ b/tools/dfuse/DfuFile.py @@ -0,0 +1,66 @@ +import argparse +import sys +import struct +import binascii + +def named(tuple,names): + return dict(zip(names,tuple)) + +def parse(fmt,data,names): + return named(struct.unpack(fmt,data),names) + +def fileunpack(f, fmt, names): + n = struct.calcsize(fmt) + return parse(fmt, f.read(n), names) + +class DfuFile: + def __init__(self, path): + self.targets = list() + self.devInfo = dict() + + try: + dfufile = open(path, 'rb') + except: + raise argparse.ArgumentTypeError('Could not open file %r' % path) + + with dfufile: + + header = fileunpack(dfufile, "<5sBLB", ('signature', 'version', 'size', 'targets')) + + if header['signature'] != b'DfuSe': + raise argparse.ArgumentTypeError('File signature does not match') + if header['version'] != 1: + raise argparse.ArgumentTypeError('Unsupport DfuSe file version') + + for t in range(header['targets']): + target_prefix = fileunpack(dfufile, "<6sBL255sLL", ('signature', 'alternate', 'named', 'name', 'size', 'elements')) + if target_prefix['signature'] != b'Target': + raise argparse.ArgumentTypeError('Target signature does not match') + + target = { + 'name': target_prefix['name'].decode('ascii').rstrip('\0'), + 'alternate': target_prefix['alternate'], + 'elements': list() + } + + for e in range(target_prefix['elements']): + element_prefix = fileunpack(dfufile,"