mirror of https://github.com/Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
395 lines
14 KiB
395 lines
14 KiB
#! /usr/bin/python |
|
|
|
""" |
|
dfuse.py - DFU (Device Firmware Upgrade) tool for STM32 Processor. |
|
"SE" in DfuSe stands for "STmicroelectronics Extention". |
|
|
|
Copyright (C) 2010, 2011 Free Software Initiative of Japan |
|
Author: NIIBE Yutaka <[email protected]> |
|
|
|
This file is a part of Gnuk, a GnuPG USB Token implementation. |
|
|
|
Gnuk is free software: you can redistribute it and/or modify it |
|
under the terms of the GNU General Public License as published by |
|
the Free Software Foundation, either version 3 of the License, or |
|
(at your option) any later version. |
|
|
|
Gnuk is distributed in the hope that it will be useful, but WITHOUT |
|
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY |
|
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public |
|
License for more details. |
|
|
|
You should have received a copy of the GNU General Public License |
|
along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
""" |
|
|
|
from intel_hex import * |
|
import sys, time, struct |
|
|
|
# INPUT: intel hex file |
|
|
|
# As of October 2010 (DfuSe V3.0.1 - 06/18/2010), it seems that |
|
# following features are not supported by DfuSe implementation on |
|
# target: |
|
# |
|
# unprotect |
|
# leave_dfu_mode |
|
# write to option bytes |
|
# erase for mass erase |
|
|
|
|
|
# See: AN3156 by STMicroelectronics |
|
|
|
import usb |
|
|
|
# check string descriptor in interrface descriptor in config descriptor: iInterface |
|
|
|
# USB DFU class, subclass, protocol |
|
DFU_CLASS = 0xFE |
|
DFU_SUBCLASS = 0x01 |
|
DFU_STM32PROTOCOL_0 = 0 |
|
DFU_STM32PROTOCOL_2 = 2 |
|
|
|
# DFU request |
|
DFU_DETACH = 0x00 |
|
DFU_DNLOAD = 0x01 |
|
DFU_UPLOAD = 0x02 |
|
DFU_GETSTATUS = 0x03 |
|
DFU_CLRSTATUS = 0x04 |
|
DFU_GETSTATE = 0x05 |
|
DFU_ABORT = 0x06 |
|
|
|
# DFU status |
|
DFU_STATUS_OK = 0x00 |
|
DFU_STATUS_ERROR_TARGET = 0x01 |
|
DFU_STATUS_ERROR_FILE = 0x02 |
|
DFU_STATUS_ERROR_WRITE = 0x03 |
|
DFU_STATUS_ERROR_ERASE = 0x04 |
|
DFU_STATUS_ERROR_CHECK_ERASED = 0x05 |
|
DFU_STATUS_ERROR_PROG = 0x06 |
|
DFU_STATUS_ERROR_VERIFY = 0x07 |
|
DFU_STATUS_ERROR_ADDRESS = 0x08 |
|
DFU_STATUS_ERROR_NOTDONE = 0x09 |
|
DFU_STATUS_ERROR_FIRMWARE = 0x0a |
|
DFU_STATUS_ERROR_VENDOR = 0x0b |
|
DFU_STATUS_ERROR_USBR = 0x0c |
|
DFU_STATUS_ERROR_POR = 0x0d |
|
DFU_STATUS_ERROR_UNKNOWN = 0x0e |
|
DFU_STATUS_ERROR_STALLEDPKT = 0x0f |
|
|
|
# DFU state |
|
STATE_APP_IDLE = 0x00 |
|
STATE_APP_DETACH = 0x01 |
|
STATE_DFU_IDLE = 0x02 |
|
STATE_DFU_DOWNLOAD_SYNC = 0x03 |
|
STATE_DFU_DOWNLOAD_BUSY = 0x04 |
|
STATE_DFU_DOWNLOAD_IDLE = 0x05 |
|
STATE_DFU_MANIFEST_SYNC = 0x06 |
|
STATE_DFU_MANIFEST = 0x07 |
|
STATE_DFU_MANIFEST_WAIT_RESET = 0x08 |
|
STATE_DFU_UPLOAD_IDLE = 0x09 |
|
STATE_DFU_ERROR = 0x0a |
|
|
|
# Return tuple of 4-bytes from integer |
|
def get_four_bytes (v): |
|
return [ v % 256, (v >> 8)%256, (v >> 16)%256, (v >> 24) ] |
|
|
|
class DFU_STM32(object): |
|
def __init__(self, device, configuration, interface): |
|
""" |
|
__init__(device, configuration, interface) -> None |
|
Initialize the device. |
|
device: usb.Device object. |
|
configuration: configuration number. |
|
interface: usb.Interface object representing the interface and altenate setting. |
|
""" |
|
if interface.interfaceClass != DFU_CLASS: |
|
raise ValueError, "Wrong interface class" |
|
if interface.interfaceSubClass != DFU_SUBCLASS: |
|
raise ValueError, "Wrong interface sub class" |
|
self.__protocol = interface.interfaceProtocol |
|
self.__devhandle = device.open() |
|
self.__devhandle.setConfiguration(configuration) |
|
self.__devhandle.claimInterface(interface) |
|
self.__devhandle.setAltInterface(0) |
|
|
|
self.__intf = interface.interfaceNumber |
|
self.__alt = interface.alternateSetting |
|
self.__conf = configuration |
|
# Initialize members |
|
self.__blocknum = 0 |
|
|
|
def ll_getdev(self): |
|
return self.__devhandle |
|
|
|
def ll_get_string(self, index): |
|
# specify buffer length for 80 |
|
return self.__devhandle.getString(index, 80) |
|
|
|
def ll_get_status(self): |
|
# Status, PollTimeout[3], State, String |
|
return self.__devhandle.controlMsg(requestType = 0xa1, |
|
request = DFU_GETSTATUS, |
|
value = 0, |
|
index = self.__intf, |
|
buffer = 6, |
|
timeout = 3000000) |
|
|
|
def ll_clear_status(self): |
|
return self.__devhandle.controlMsg(requestType = 0x21, |
|
request = DFU_CLRSTATUS, |
|
value = 0, |
|
index = self.__intf, |
|
buffer = None) |
|
|
|
# Upload: TARGET -> HOST |
|
def ll_upload_block(self, block_num): |
|
return self.__devhandle.controlMsg(requestType = 0xa1, |
|
request = DFU_UPLOAD, |
|
value = block_num, |
|
index = self.__intf, |
|
buffer = 1024, |
|
timeout = 3000000) |
|
|
|
# Download: HOST -> TARGET |
|
def ll_download_block(self, block_num, block): |
|
return self.__devhandle.controlMsg(requestType = 0x21, |
|
request = DFU_DNLOAD, |
|
value = block_num, |
|
index = self.__intf, |
|
buffer = block) |
|
|
|
def dfuse_read_memory(self): |
|
blocknum = self.__blocknum |
|
self.__blocknum = self.__blocknum + 1 |
|
try: |
|
block = self.ll_upload_block(blocknum) |
|
return block |
|
except: |
|
s = self.ll_get_status() |
|
while s[4] == STATE_DFU_DOWNLOAD_BUSY: |
|
time.sleep(0.1) |
|
s = self.ll_get_status() |
|
raise ValueError, "Read memory failed (%d)" % s[0] |
|
|
|
def dfuse_set_address_pointer(self, address): |
|
bytes = get_four_bytes (address) |
|
self.__blocknum = 2 |
|
self.ll_download_block(0, [0x21] + bytes) |
|
s = self.ll_get_status() |
|
while s[4] == STATE_DFU_DOWNLOAD_BUSY: |
|
time.sleep(0.1) |
|
s = self.ll_get_status() |
|
if s[4] != STATE_DFU_DOWNLOAD_IDLE: |
|
raise ValueError, "Set Address Pointer failed" |
|
|
|
def dfuse_erase(self, address): |
|
bytes = get_four_bytes (address) |
|
self.ll_download_block(0, [0x41] + bytes) |
|
s = self.ll_get_status() |
|
while s[4] == STATE_DFU_DOWNLOAD_BUSY: |
|
time.sleep(0.1) |
|
s = self.ll_get_status() |
|
if s[4] != STATE_DFU_DOWNLOAD_IDLE: |
|
raise ValueError, "Erase failed" |
|
|
|
def dfuse_write_memory(self, block): |
|
blocknum = self.__blocknum |
|
self.__blocknum = self.__blocknum + 1 |
|
self.ll_download_block(blocknum, block) |
|
s = self.ll_get_status() |
|
while s[4] == STATE_DFU_DOWNLOAD_BUSY: |
|
time.sleep(0.1) |
|
s = self.ll_get_status() |
|
if s[4] != STATE_DFU_DOWNLOAD_IDLE: |
|
raise ValueError, "Write memory failed" |
|
|
|
def download(self, ih): |
|
# First, erase pages |
|
sys.stdout.write("Erasing: ") |
|
sys.stdout.flush() |
|
last_addr = 0 |
|
for start_addr in sorted(ih.memory.keys()): |
|
data = ih.memory[start_addr] |
|
end_addr = start_addr + len(data) |
|
addr = start_addr & 0xfffffc00 |
|
if not last_addr == 0: |
|
i = 0 |
|
if last_addr > addr: |
|
addr = last_addr |
|
else: |
|
while last_addr < addr: |
|
self.dfuse_erase(last_addr) |
|
if i & 0x03 == 0x03: |
|
sys.stdout.write(".") |
|
sys.stdout.flush() |
|
last_addr += 1024 |
|
i += 1 |
|
i = 0 |
|
while addr < end_addr: |
|
self.dfuse_erase(addr) |
|
if i & 0x03 == 0x03: |
|
sys.stdout.write("#") |
|
sys.stdout.flush() |
|
addr += 1024 |
|
i += 1 |
|
last_addr = addr |
|
sys.stdout.write("\n") |
|
sys.stdout.flush() |
|
# Then, write pages |
|
sys.stdout.write("Writing: ") |
|
sys.stdout.flush() |
|
last_addr = 0 |
|
for start_addr in sorted(ih.memory.keys()): |
|
data = ih.memory[start_addr] |
|
end_addr = start_addr + len(data) |
|
addr = start_addr & 0xfffffc00 |
|
if not last_addr == 0: |
|
i = 0 |
|
while last_addr < addr: |
|
if i & 0x03 == 0x03: |
|
sys.stdout.write(".") |
|
sys.stdout.flush() |
|
last_addr += 1024 |
|
i += 1 |
|
i = 0 |
|
if addr != start_addr: |
|
self.dfuse_set_address_pointer(start_addr) |
|
self.dfuse_write_memory(data[0:(addr + 1024 - start_addr)]) |
|
data = data[(addr + 1024 - start_addr):] |
|
addr += 1024 |
|
self.dfuse_set_address_pointer(addr) |
|
while addr < end_addr: |
|
self.dfuse_write_memory(data[i*1024:(i+1)*1024]) |
|
if i & 0x03 == 0x03: |
|
sys.stdout.write("#") |
|
sys.stdout.flush() |
|
addr += 1024 |
|
i += 1 |
|
last_addr = addr |
|
if self.__protocol == DFU_STM32PROTOCOL_0: |
|
# 0-length write at the end |
|
self.ll_download_block(self.__blocknum, None) |
|
s = self.ll_get_status() |
|
if s[4] == STATE_DFU_MANIFEST: |
|
time.sleep(1) |
|
try: |
|
s = self.ll_get_status() |
|
except: |
|
self.__devhandle.reset() |
|
elif s[4] == STATE_DFU_MANIFEST_WAIT_RESET: |
|
self.__devhandle.reset() |
|
elif s[4] != STATE_DFU_IDLE: |
|
raise ValueError, "write failed (%d)." % s[4] |
|
else: |
|
self.ll_clear_status() |
|
self.ll_clear_status() |
|
sys.stdout.write("\n") |
|
sys.stdout.flush() |
|
|
|
def verify(self, ih): |
|
s = self.ll_get_status() |
|
if s[4] != STATE_DFU_IDLE: |
|
self.ll_clear_status() |
|
# Read pages |
|
sys.stdout.write("Reading: ") |
|
sys.stdout.flush() |
|
last_addr = 0 |
|
for start_addr in sorted(ih.memory.keys()): |
|
data = ih.memory[start_addr] |
|
end_addr = start_addr + len(data) |
|
addr = start_addr & 0xfffffc00 |
|
if not last_addr == 0: |
|
i = 0 |
|
while last_addr < addr: |
|
if i & 0x03 == 0x03: |
|
sys.stdout.write(".") |
|
sys.stdout.flush() |
|
last_addr += 1024 |
|
i += 1 |
|
if addr != start_addr: |
|
self.dfuse_set_address_pointer(addr) |
|
self.ll_clear_status() |
|
self.ll_clear_status() |
|
block = self.dfuse_read_memory() |
|
j = 0 |
|
for c in data[0:(addr + 1024 - start_addr)]: |
|
if (ord(c)&0xff) != block[j + start_addr - addr]: |
|
raise ValueError, "verify failed at %08x" % (addr + i*1024+j) |
|
j += 1 |
|
data = data[(addr + 1024 - start_addr):] |
|
addr += 1024 |
|
self.ll_clear_status() |
|
self.ll_clear_status() |
|
self.dfuse_set_address_pointer(addr) |
|
self.ll_clear_status() |
|
self.ll_clear_status() |
|
i = 0 |
|
while addr < end_addr: |
|
block = self.dfuse_read_memory() |
|
j = 0 |
|
for c in data[i*1024:(i+1)*1024]: |
|
if (ord(c)&0xff) != block[j]: |
|
raise ValueError, "verify failed at %08x" % (addr + i*1024+j) |
|
j += 1 |
|
if i & 0x03 == 0x03: |
|
sys.stdout.write("#") |
|
sys.stdout.flush() |
|
addr += 1024 |
|
i += 1 |
|
last_addr = addr |
|
self.ll_clear_status() |
|
self.ll_clear_status() |
|
self.ll_clear_status() |
|
sys.stdout.write("\n") |
|
sys.stdout.flush() |
|
|
|
busses = usb.busses() |
|
|
|
# 0483: SGS Thomson Microelectronics |
|
# df11: DfuSe |
|
USB_VENDOR_STMICRO=0x0483 |
|
USB_PRODUCT_DFUSE=0xdf11 |
|
|
|
def get_device(): |
|
for bus in busses: |
|
devices = bus.devices |
|
for dev in devices: |
|
if dev.idVendor != USB_VENDOR_STMICRO: |
|
continue |
|
if dev.idProduct != USB_PRODUCT_DFUSE: |
|
continue |
|
for config in dev.configurations: |
|
for intf in config.interfaces: |
|
for alt in intf: |
|
if alt.interfaceClass == DFU_CLASS and \ |
|
alt.interfaceSubClass == DFU_SUBCLASS and \ |
|
(alt.interfaceProtocol == DFU_STM32PROTOCOL_0 or \ |
|
alt.interfaceProtocol == DFU_STM32PROTOCOL_2): |
|
return dev, config, alt |
|
raise ValueError, "Device not found" |
|
|
|
def main(filename): |
|
dev, config, intf = get_device() |
|
print "Device:", dev.filename |
|
print "Configuration", config.value |
|
print "Interface", intf.interfaceNumber |
|
dfu = DFU_STM32(dev, config, intf) |
|
print dfu.ll_get_string(intf.iInterface) |
|
s = dfu.ll_get_status() |
|
if s[4] == STATE_DFU_ERROR: |
|
dfu.ll_clear_status() |
|
s = dfu.ll_get_status() |
|
print s |
|
if s[4] == STATE_DFU_IDLE: |
|
exit |
|
transfer_size = 1024 |
|
if s[0] != DFU_STATUS_OK: |
|
print s |
|
exit |
|
ih = intel_hex(filename) |
|
dfu.download(ih) |
|
dfu.verify(ih) |
|
|
|
if __name__ == '__main__': |
|
main(sys.argv[1])
|
|
|