From 4c6f7354522ff6a69fac0333347598dd61c9f9c5 Mon Sep 17 00:00:00 2001 From: Uwe Bonnes Date: Tue, 26 Sep 2017 19:40:27 +0200 Subject: [PATCH 1/6] stm32_mem.py: Handle multiple devices. --- scripts/stm32_mem.py | 46 +++++++++++++++++++++++++++++++++----------- 1 file changed, 35 insertions(+), 11 deletions(-) diff --git a/scripts/stm32_mem.py b/scripts/stm32_mem.py index 780da327..52fea0e7 100755 --- a/scripts/stm32_mem.py +++ b/scripts/stm32_mem.py @@ -81,29 +81,52 @@ if __name__ == "__main__": parser.add_argument("-s", "--serial_target", help="Match Serial Number") args = parser.parse_args() devs = dfu.finddevs() + bmp = 0 if not devs: - print "No devices found!" + print "No DFU devices found!" exit(-1) for dev in devs: dfudev = dfu.dfu_device(*dev) man = dfudev.handle.getString(dfudev.dev.iManufacturer, 30) - product = dfudev.handle.getString(dfudev.dev.iProduct, 64) + if man == "Black Sphere Technologies": bmp = bmp + 1 + if man == "STMicroelectronics": bmp = bmp + 1 + if bmp == 0 : + print "No compatible device found\n" + exit(-1) + if bmp > 1 and not args.serial_target : + print "Found multiple devices:\n" + for dev in devs: + dfudev = dfu.dfu_device(*dev) + man = dfudev.handle.getString(dfudev.dev.iManufacturer, 30) + product = dfudev.handle.getString(dfudev.dev.iProduct, 96) + serial_no = dfudev.handle.getString(dfudev.dev.iSerialNumber, 30) + print "Device ID:\t %04x:%04x" % (dfudev.dev.idVendor, dfudev.dev.idProduct) + print "Manufacturer:\t %s" % man + print "Product:\t %s" % product + print "Serial:\t\t %s\n" % serial_no + print "Select device with serial number!" + exit (-1) + + for dev in devs: + dfudev = dfu.dfu_device(*dev) + man = dfudev.handle.getString(dfudev.dev.iManufacturer, 30) + product = dfudev.handle.getString(dfudev.dev.iProduct, 96) serial_no = dfudev.handle.getString(dfudev.dev.iSerialNumber, 30) if args.serial_target: - if man == "Black Sphere Technologies" and serial_no == args.serial_target: break + if man == "Black Sphere Technologies" and serial_no == args.serial_target: break if man == "STMicroelectronics" and serial_no == args.serial_target: break - else: + else: if man == "Black Sphere Technologies": break if man == "STMicroelectronics": break - print "Device %s: ID %04x:%04x %s - %s\n\tSerial %s" % ( - dfudev.dev.filename, dfudev.dev.idVendor, - dfudev.dev.idProduct, man, product, serial_no) - - if args.serial_target and serial_no != args.serial_target: - print "Serial number doesn't match!\n" - exit(-2) + print "Device ID:\t %04x:%04x" % (dfudev.dev.idVendor, dfudev.dev.idProduct) + print "Manufacturer:\t %s" % man + print "Product:\t %s" % product + print "Serial:\t\t %s\n" % serial_no + if args.serial_target and serial_no != args.serial_target: + print "Serial number doesn't match!\n" + exit(-2) try: state = dfudev.get_state() except: @@ -118,6 +141,7 @@ if __name__ == "__main__": bin = open(args.progfile, "rb").read() + product = dfudev.handle.getString(dfudev.dev.iProduct, 64) if "F4" in product: addr = 0x8004000 else: From 4f3f4cb89812e31093f4c406f751e320a22af246 Mon Sep 17 00:00:00 2001 From: Uwe Bonnes Date: Thu, 28 Sep 2017 01:55:59 +0200 Subject: [PATCH 2/6] stm32_mem.py: Deny to work with the STM DFU bootloader To support the STM DFU bootloader, the interface descriptor needs to be evaluated. Erase may only be called once per sector. --- scripts/stm32_mem.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/scripts/stm32_mem.py b/scripts/stm32_mem.py index 52fea0e7..6ed71261 100755 --- a/scripts/stm32_mem.py +++ b/scripts/stm32_mem.py @@ -2,6 +2,7 @@ # # stm32_mem.py: STM32 memory access using USB DFU class # Copyright (C) 2011 Black Sphere Technologies +# Copyright (C) 2017 Uwe Bonnes (bon@elektron.ikp.physik.tu-darmstadt.de) # Written by Gareth McMullin # # This program is free software: you can redistribute it and/or modify @@ -90,7 +91,6 @@ if __name__ == "__main__": dfudev = dfu.dfu_device(*dev) man = dfudev.handle.getString(dfudev.dev.iManufacturer, 30) if man == "Black Sphere Technologies": bmp = bmp + 1 - if man == "STMicroelectronics": bmp = bmp + 1 if bmp == 0 : print "No compatible device found\n" exit(-1) @@ -115,11 +115,8 @@ if __name__ == "__main__": serial_no = dfudev.handle.getString(dfudev.dev.iSerialNumber, 30) if args.serial_target: if man == "Black Sphere Technologies" and serial_no == args.serial_target: break - if man == "STMicroelectronics" and serial_no == args.serial_target: break else: if man == "Black Sphere Technologies": break - if man == "STMicroelectronics": break - print "Device ID:\t %04x:%04x" % (dfudev.dev.idVendor, dfudev.dev.idProduct) print "Manufacturer:\t %s" % man print "Product:\t %s" % product @@ -141,7 +138,7 @@ if __name__ == "__main__": bin = open(args.progfile, "rb").read() - product = dfudev.handle.getString(dfudev.dev.iProduct, 64) + product = dfudev.handle.getString(dfudev.dev.iProduct, 64) if "F4" in product: addr = 0x8004000 else: @@ -150,6 +147,10 @@ if __name__ == "__main__": print ("Programming memory at 0x%08X\r" % addr), stdout.flush() try: +# STM DFU bootloader erases always. +# BPM Bootloader only erases once per sector +# To support the STM DFU bootloader, the interface descriptor must +# get evaluated and erase called only once per sector! stm32_erase(dfudev, addr) except: print "\nErase Timed out\n" @@ -160,7 +161,6 @@ if __name__ == "__main__": print "\nSet Address Timed out\n" break stm32_write(dfudev, bin[:1024]) - bin = bin[1024:] addr += 1024 From c41dfaef9a7669e4e64e30ecb6a3232cd2404d50 Mon Sep 17 00:00:00 2001 From: Uwe Bonnes Date: Wed, 27 Sep 2017 21:38:54 +0200 Subject: [PATCH 3/6] stm32_mem.py: Run automatically after switching to DFU mode. --- scripts/dfu.py | 2 ++ scripts/stm32_mem.py | 46 +++++++++++++++++++++++++++----------------- 2 files changed, 30 insertions(+), 18 deletions(-) diff --git a/scripts/dfu.py b/scripts/dfu.py index 27c25dba..5e1e5925 100644 --- a/scripts/dfu.py +++ b/scripts/dfu.py @@ -84,6 +84,8 @@ class dfu_device(object): self.index = self.iface.interfaceNumber else: self.index = self.iface + def release(self): + self.handle.releaseInterface() def detach(self, wTimeout=255): self.handle.controlMsg(usb.ENDPOINT_OUT | usb.TYPE_CLASS | usb.RECIP_INTERFACE, DFU_DETACH, diff --git a/scripts/stm32_mem.py b/scripts/stm32_mem.py index 6ed71261..a824ad17 100755 --- a/scripts/stm32_mem.py +++ b/scripts/stm32_mem.py @@ -69,18 +69,7 @@ def stm32_manifest(dev): sleep(status.bwPollTimeout / 1000.0) if status.bState == dfu.STATE_DFU_MANIFEST: break - -if __name__ == "__main__": - print - print "USB Device Firmware Upgrade - Host Utility -- version 1.2" - print "Copyright (C) 2011 Black Sphere Technologies" - print "License GPLv3+: GNU GPL version 3 or later " - print - - parser = argparse.ArgumentParser() - parser.add_argument("progfile", help="Binary file to program") - parser.add_argument("-s", "--serial_target", help="Match Serial Number") - args = parser.parse_args() +def stm32_scan(args): devs = dfu.finddevs() bmp = 0 if not devs: @@ -120,10 +109,24 @@ if __name__ == "__main__": print "Device ID:\t %04x:%04x" % (dfudev.dev.idVendor, dfudev.dev.idProduct) print "Manufacturer:\t %s" % man print "Product:\t %s" % product - print "Serial:\t\t %s\n" % serial_no + print "Serial:\t\t %s" % serial_no if args.serial_target and serial_no != args.serial_target: print "Serial number doesn't match!\n" exit(-2) + return dfudev + +if __name__ == "__main__": + print + print "USB Device Firmware Upgrade - Host Utility -- version 1.2" + print "Copyright (C) 2011 Black Sphere Technologies" + print "License GPLv3+: GNU GPL version 3 or later " + print + + parser = argparse.ArgumentParser() + parser.add_argument("progfile", help="Binary file to program") + parser.add_argument("-s", "--serial_target", help="Match Serial Number") + args = parser.parse_args() + dfudev = stm32_scan(args) try: state = dfudev.get_state() except: @@ -131,11 +134,18 @@ if __name__ == "__main__": state = dfu.STATE_APP_IDLE if state == dfu.STATE_APP_IDLE: dfudev.detach() - print "Run again to upgrade firmware." - exit(0) - + dfudev.release() + print "Invoking DFU Device" + timeout = 0 + while True : + sleep(0.5) + timeout = timeout + 0.5 + dfudev = stm32_scan(args) + if dfudev: break + if timeout > 5 : + print "Error: DFU device did not appear" + exit(-1) dfudev.make_idle() - bin = open(args.progfile, "rb").read() product = dfudev.handle.getString(dfudev.dev.iProduct, 64) @@ -152,7 +162,7 @@ if __name__ == "__main__": # To support the STM DFU bootloader, the interface descriptor must # get evaluated and erase called only once per sector! stm32_erase(dfudev, addr) - except: + except: print "\nErase Timed out\n" break try: From 613208c93994ceea67db636140653f61a73937bb Mon Sep 17 00:00:00 2001 From: Uwe Bonnes Date: Thu, 28 Sep 2017 13:01:59 +0200 Subject: [PATCH 4/6] stm32_mem: Allow to switch from dfu to application without flashing. --- scripts/stm32_mem.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/scripts/stm32_mem.py b/scripts/stm32_mem.py index a824ad17..b742ebfe 100755 --- a/scripts/stm32_mem.py +++ b/scripts/stm32_mem.py @@ -125,11 +125,13 @@ if __name__ == "__main__": parser = argparse.ArgumentParser() parser.add_argument("progfile", help="Binary file to program") parser.add_argument("-s", "--serial_target", help="Match Serial Number") + parser.add_argument("-m", "--manifest", help="Start application, if in DFU mode", action='store_true') args = parser.parse_args() dfudev = stm32_scan(args) try: state = dfudev.get_state() except: + if args.manifest : exit(0) print "Failed to read device state! Assuming APP_IDLE" state = dfu.STATE_APP_IDLE if state == dfu.STATE_APP_IDLE: @@ -145,6 +147,10 @@ if __name__ == "__main__": if timeout > 5 : print "Error: DFU device did not appear" exit(-1) + if args.manifest : + stm32_manifest(dfudev) + print "Invoking Application Device" + exit(0) dfudev.make_idle() bin = open(args.progfile, "rb").read() From 7cc9ee9d7a5cda4015a9575377cf49c1ed78a14a Mon Sep 17 00:00:00 2001 From: Uwe Bonnes Date: Tue, 26 Sep 2017 18:55:26 +0200 Subject: [PATCH 5/6] stm32_mem.py: Verify after write when bootloader supports upload This should help people using STM32F103C8 above 64 k. --- scripts/stm32_mem.py | 49 ++++++++++++++++++++++++++++++++++++++------ 1 file changed, 43 insertions(+), 6 deletions(-) diff --git a/scripts/stm32_mem.py b/scripts/stm32_mem.py index b742ebfe..edb5ef2b 100755 --- a/scripts/stm32_mem.py +++ b/scripts/stm32_mem.py @@ -58,7 +58,17 @@ def stm32_write(dev, data): sleep(status.bwPollTimeout / 1000.0) if status.bState == dfu.STATE_DFU_DOWNLOAD_IDLE: break - + +def stm32_read(dev): + data = dev.upload(2, 1024) + while True: + status = dev.get_status() + if status.bState == dfu.STATE_DFU_DOWNLOAD_BUSY: + sleep(status.bwPollTimeout / 1000.0) + if status.bState == dfu.STATE_DFU_UPLOAD_IDLE: + break + return data + def stm32_manifest(dev): dev.download(0, "") while True: @@ -152,13 +162,15 @@ if __name__ == "__main__": print "Invoking Application Device" exit(0) dfudev.make_idle() - bin = open(args.progfile, "rb").read() + file = open(args.progfile, "rb") + bin = file.read() product = dfudev.handle.getString(dfudev.dev.iProduct, 64) if "F4" in product: - addr = 0x8004000 + start = 0x8004000 else: - addr = 0x8002000 + start = 0x8002000 + addr = start while bin: print ("Programming memory at 0x%08X\r" % addr), stdout.flush() @@ -179,7 +191,32 @@ if __name__ == "__main__": stm32_write(dfudev, bin[:1024]) bin = bin[1024:] addr += 1024 - + file.seek(0) + bin = file.read() + len = len(bin) + addr = start + print + while bin: + try: + stm32_set_address(dfudev, addr) + data = stm32_read(dfudev) + except: +# Abort silent if bootloader does not support upload + break + print ("Verifying memory at 0x%08X\r" % addr), + stdout.flush() + if len > 1024 : + size = 1024 + else : + size = len + if bin[:size] != bytearray(data[:size]) : + print ("\nMitmatch in block at 0x%08X" % addr) + break; + bin = bin[1024:] + addr += 1024 + len -= 1024 + if len <= 0 : + print "\nVerified!" stm32_manifest(dfudev) - print "\nAll operations complete!\n" + print "All operations complete!\n" From 2b2b6d8f31a21a6c456ac612f92ac47d66be7f59 Mon Sep 17 00:00:00 2001 From: Uwe Bonnes Date: Mon, 2 Oct 2017 15:50:40 +0200 Subject: [PATCH 6/6] stm32_mem.py: Allow to set start address. --- scripts/stm32_mem.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/scripts/stm32_mem.py b/scripts/stm32_mem.py index edb5ef2b..33d35f9d 100755 --- a/scripts/stm32_mem.py +++ b/scripts/stm32_mem.py @@ -135,6 +135,7 @@ if __name__ == "__main__": parser = argparse.ArgumentParser() parser.add_argument("progfile", help="Binary file to program") parser.add_argument("-s", "--serial_target", help="Match Serial Number") + parser.add_argument("-a", "--address", help="Start address for firmware") parser.add_argument("-m", "--manifest", help="Start application, if in DFU mode", action='store_true') args = parser.parse_args() dfudev = stm32_scan(args) @@ -166,10 +167,13 @@ if __name__ == "__main__": bin = file.read() product = dfudev.handle.getString(dfudev.dev.iProduct, 64) - if "F4" in product: - start = 0x8004000 - else: - start = 0x8002000 + if args.address : + start = int(args.address, 0) + else : + if "F4" in product: + start = 0x8004000 + else: + start = 0x8002000 addr = start while bin: print ("Programming memory at 0x%08X\r" % addr),