Advertisement
Guest User

Untitled

a guest
Jul 9th, 2017
514
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 20.54 KB | None | 0 0
  1. #!/usr/bin/env python
  2.  
  3. # -*- coding: utf-8 -*-
  4. # vim: sw=4:ts=4:si:et:enc=utf-8
  5.  
  6. # Author: Ivan A-R <ivan@tuxotronic.org>
  7. # With hacky error recovery by Gordon Williams <gw@pur3.co.uk>
  8. # Project page: http://tuxotronic.org/wiki/projects/stm32loader
  9. #
  10. # This file is part of stm32loader.
  11. #
  12. # stm32loader is free software; you can redistribute it and/or modify it under
  13. # the terms of the GNU General Public License as published by the Free
  14. # Software Foundation; either version 3, or (at your option) any later
  15. # version.
  16. #
  17. # stm32loader is distributed in the hope that it will be useful, but WITHOUT ANY
  18. # WARRANTY; without even the implied warranty of MERCHANTABILITY or
  19. # FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  20. # for more details.
  21. #
  22. # You should have received a copy of the GNU General Public License
  23. # along with stm32loader; see the file COPYING3.  If not see
  24. # <http://www.gnu.org/licenses/>.
  25.  
  26. from __future__ import print_function
  27.  
  28. import sys, getopt
  29. import serial
  30. import time
  31. import glob
  32. import time
  33. import tempfile
  34. import os
  35. import subprocess
  36.  
  37. try:
  38.     from progressbar import *
  39.     usepbar = 1
  40. except:
  41.     usepbar = 0
  42.  
  43. # Verbose level
  44. QUIET = 5
  45.  
  46. def mdebug(level, message):
  47.     if QUIET >= level:
  48.         print(message, file=sys.stderr)
  49.  
  50. # Takes chip IDs (obtained via Get ID command) to human-readable names
  51. CHIP_ID_STRS = {0x410: 'STM32F1, performance, medium-density',
  52.                 0x411: 'STM32F2',
  53.                 0x412: 'STM32F1, performance, low-density',
  54.                 0x413: 'STM32F4',
  55.                 0x414: 'STM32F1, performance, high-density',
  56.                 0x416: 'STM32L1, performance, medium-density',
  57.                 0x418: 'STM32F1, connectivity',
  58.                 0x420: 'STM32F1, value, medium-density',
  59.                 0x428: 'STM32F1, value, high-density',
  60.                 0x430: 'STM32F1, performance, XL-density'}
  61.  
  62. class CmdException(Exception):
  63.     pass
  64.  
  65. class CommandInterface(object):
  66.     def open(self, aport='/dev/tty.usbserial-FTD3TMCH', abaudrate=115200) :
  67.         self.sp = serial.Serial(
  68.             port=aport,
  69.             baudrate=abaudrate,     # baudrate
  70.             bytesize=8,             # number of databits
  71.             parity=serial.PARITY_EVEN,
  72.             stopbits=1,
  73.             xonxoff=0,              # enable software flow control
  74.             rtscts=0,               # disable RTS/CTS flow control
  75.             timeout=0.5             # set a timeout value, None for waiting forever
  76.         )
  77.  
  78.  
  79.     def _wait_for_ack(self, info="", timeout=0):
  80.         stop = time.time() + timeout
  81.         got = None
  82.         while not got:
  83.             got = self.sp.read(1)
  84.             if time.time() > stop:
  85.                 break
  86.  
  87.         if not got:
  88.             raise CmdException("No response to %s" % info)
  89.  
  90.         # wait for ask
  91.         ask = ord(got)
  92.  
  93.         if ask == 0x79:
  94.             # ACK
  95.             return 1
  96.         elif ask == 0x1F:
  97.             # NACK
  98.             raise CmdException("Chip replied with a NACK during %s" % info)
  99.  
  100.         # Unknown response
  101.         raise CmdException("Unrecognised response 0x%x to %s" % (ask, info))
  102.  
  103.     def reset(self):
  104.         self.sp.setDTR(0)
  105.         time.sleep(0.1)
  106.         self.sp.setDTR(1)
  107.         time.sleep(0.5)
  108.  
  109.     def initChip(self):
  110.         # Set boot
  111.         self.sp.setRTS(0)
  112.         self.reset()
  113.  
  114.         # Be a bit more persistent when trying to initialise the chip
  115.         stop = time.time() + 5.0
  116.  
  117.         while time.time() <= stop:
  118.             self.sp.write('\x7f')
  119.  
  120.             got = self.sp.read()
  121.  
  122.             # The chip will ACK a sync the very first time and
  123.             # NACK it every time afterwards
  124.             if got and got in '\x79\x1f':
  125.                 # Synced up
  126.                 return
  127.  
  128.         raise CmdException('No response while trying to sync')
  129.  
  130.     def releaseChip(self):
  131.         self.sp.setRTS(1)
  132.         self.reset()
  133.  
  134.     def cmdGeneric(self, cmd):
  135.         self.sp.write(chr(cmd))
  136.         self.sp.write(chr(cmd ^ 0xFF)) # Control byte
  137.         return self._wait_for_ack(hex(cmd))
  138.  
  139.     def cmdGet(self):
  140.         if self.cmdGeneric(0x00):
  141.             mdebug(10, "*** Get command");
  142.             len = ord(self.sp.read())
  143.             version = ord(self.sp.read())
  144.             mdebug(10, "    Bootloader version: "+hex(version))
  145.             dat = map(lambda c: hex(ord(c)), self.sp.read(len))
  146.             mdebug(10, "    Available commands: "+str(dat))
  147.             self._wait_for_ack("0x00 end")
  148.             return version
  149.         else:
  150.             raise CmdException("Get (0x00) failed")
  151.  
  152.     def cmdGetVersion(self):
  153.         if self.cmdGeneric(0x01):
  154.             mdebug(10, "*** GetVersion command")
  155.             version = ord(self.sp.read())
  156.             self.sp.read(2)
  157.             self._wait_for_ack("0x01 end")
  158.             mdebug(10, "    Bootloader version: "+hex(version))
  159.             return version
  160.         else:
  161.             raise CmdException("GetVersion (0x01) failed")
  162.  
  163.     def cmdGetID(self):
  164.         if self.cmdGeneric(0x02):
  165.             mdebug(10, "*** GetID command")
  166.             len = ord(self.sp.read())
  167.             id = self.sp.read(len+1)
  168.             self._wait_for_ack("0x02 end")
  169.             return id
  170.         else:
  171.             raise CmdException("GetID (0x02) failed")
  172.  
  173.  
  174.     def _encode_addr(self, addr):
  175.         byte3 = (addr >> 0) & 0xFF
  176.         byte2 = (addr >> 8) & 0xFF
  177.         byte1 = (addr >> 16) & 0xFF
  178.         byte0 = (addr >> 24) & 0xFF
  179.         crc = byte0 ^ byte1 ^ byte2 ^ byte3
  180.         return (chr(byte0) + chr(byte1) + chr(byte2) + chr(byte3) + chr(crc))
  181.  
  182.  
  183.     def cmdReadMemory(self, addr, lng):
  184.         assert(lng <= 256)
  185.         if self.cmdGeneric(0x11):
  186.             mdebug(10, "*** ReadMemory command")
  187.             self.sp.write(self._encode_addr(addr))
  188.             self._wait_for_ack("0x11 address failed")
  189.             N = (lng - 1) & 0xFF
  190.             crc = N ^ 0xFF
  191.             self.sp.write(chr(N) + chr(crc))
  192.             self._wait_for_ack("0x11 length failed")
  193.             return map(lambda c: ord(c), self.sp.read(lng))
  194.         else:
  195.             raise CmdException("ReadMemory (0x11) failed")
  196.  
  197.  
  198.     def cmdGo(self, addr):
  199.         if self.cmdGeneric(0x21):
  200.             mdebug(10, "*** Go command")
  201.             self.sp.write(self._encode_addr(addr))
  202.             self._wait_for_ack("0x21 go failed")
  203.         else:
  204.             raise CmdException("Go (0x21) failed")
  205.  
  206.  
  207.     def cmdWriteMemory(self, addr, data):
  208.         assert(len(data) <= 256)
  209.         if self.cmdGeneric(0x31):
  210.             mdebug(10, "*** Write memory command")
  211.             self.sp.write(self._encode_addr(addr))
  212.             self._wait_for_ack("0x31 address failed")
  213.             #map(lambda c: hex(ord(c)), data)
  214.             lng = (len(data)-1) & 0xFF
  215.             mdebug(10, "    %s bytes to write" % [lng+1]);
  216.             self.sp.write(chr(lng)) # len really
  217.             crc = 0xFF
  218.             try:
  219.               datastr = ""
  220.               for c in data:
  221.                   crc = crc ^ c
  222.                   datastr = datastr+chr(c)
  223.               datastr = datastr + chr(crc)
  224.               self.sp.write(datastr)
  225.               self._wait_for_ack("0x31 programming failed")
  226.               mdebug(10, "    Write memory done")
  227.             except:
  228.               mdebug(5, "    WRITE FAIL - try and recover")
  229.               for c in data:
  230.                 self.sp.write(chr(255))
  231.               mdebug(5, "    WRITE FAIL - wait")
  232.               stop = time.time() + 1
  233.               while time.time() < stop:
  234.                 if self.sp.inWaiting()>0: self.sp.read(self.sp.inWaiting())
  235.               mdebug(5, "    WRITE FAIL - retry")
  236.               self.cmdWriteMemory(addr, data)
  237.         else:
  238.             raise CmdException("Write memory (0x31) failed")
  239.  
  240.  
  241.     def cmdEraseMemory(self, sectors = None):
  242.         if self.cmdGeneric(0x43):
  243.             mdebug(10, "*** Erase memory command")
  244.             if sectors is None:
  245.                 # Global erase
  246.                 self.sp.write(chr(0xFF))
  247.                 self.sp.write(chr(0x00))
  248.             else:
  249.                 # Sectors erase
  250.                 self.sp.write(chr((len(sectors)-1) & 0xFF))
  251.                 crc = 0xFF
  252.                 for c in sectors:
  253.                     crc = crc ^ c
  254.                     self.sp.write(chr(c))
  255.                 self.sp.write(chr(crc))
  256.             self._wait_for_ack("0x43 erasing failed")
  257.             mdebug(10, "    Erase memory done")
  258.         else:
  259.             raise CmdException("Erase memory (0x43) failed")
  260.  
  261.  
  262.     # TODO support for non-global mass erase
  263.     GLOBAL_ERASE_TIMEOUT_SECONDS = 20   # This takes a while
  264.     def cmdExtendedEraseMemory(self):
  265.         if self.cmdGeneric(0x44):
  266.             mdebug(10, "*** Extended erase memory command")
  267.             # Global mass erase
  268.             mdebug(5, "Global mass erase; this may take a while")
  269.             self.sp.write(chr(0xFF))
  270.             self.sp.write(chr(0xFF))
  271.             # Checksum
  272.             self.sp.write(chr(0x00))
  273.             self._wait_for_ack("0x44 extended erase failed",
  274.                                timeout=self.GLOBAL_ERASE_TIMEOUT_SECONDS)
  275.             mdebug(10, "    Extended erase memory done")
  276.         else:
  277.             raise CmdException("Extended erase memory (0x44) failed")
  278.  
  279.  
  280.     def cmdWriteProtect(self, sectors):
  281.         if self.cmdGeneric(0x63):
  282.             mdebug(10, "*** Write protect command")
  283.             self.sp.write(chr((len(sectors)-1) & 0xFF))
  284.             crc = 0xFF
  285.             for c in sectors:
  286.                 crc = crc ^ c
  287.                 self.sp.write(chr(c))
  288.             self.sp.write(chr(crc))
  289.             self._wait_for_ack("0x63 write protect failed")
  290.             mdebug(10, "    Write protect done")
  291.         else:
  292.             raise CmdException("Write Protect memory (0x63) failed")
  293.  
  294.     def cmdWriteUnprotect(self):
  295.         if self.cmdGeneric(0x73):
  296.             mdebug(10, "*** Write Unprotect command")
  297.             self._wait_for_ack("0x73 write unprotect failed")
  298.             self._wait_for_ack("0x73 write unprotect 2 failed")
  299.             mdebug(10, "    Write Unprotect done")
  300.         else:
  301.             raise CmdException("Write Unprotect (0x73) failed")
  302.  
  303.     def cmdReadoutProtect(self):
  304.         if self.cmdGeneric(0x82):
  305.             mdebug(10, "*** Readout protect command")
  306.             self._wait_for_ack("0x82 readout protect failed")
  307.             self._wait_for_ack("0x82 readout protect 2 failed")
  308.             mdebug(10, "    Read protect done")
  309.         else:
  310.             raise CmdException("Readout protect (0x82) failed")
  311.  
  312.     def cmdReadoutUnprotect(self):
  313.         if self.cmdGeneric(0x92):
  314.             mdebug(10, "*** Readout Unprotect command")
  315.             self._wait_for_ack("0x92 readout unprotect failed")
  316.             self._wait_for_ack("0x92 readout unprotect 2 failed")
  317.             mdebug(10, "    Read Unprotect done")
  318.         else:
  319.             raise CmdException("Readout unprotect (0x92) failed")
  320.  
  321.  
  322. # Complex commands section
  323.  
  324.     def readMemory(self, addr, lng):
  325.         data = []
  326.         if usepbar:
  327.             widgets = ['Reading: ', Percentage(),', ', ETA(), ' ', Bar()]
  328.             pbar = ProgressBar(widgets=widgets,maxval=lng, term_width=79).start()
  329.  
  330.         while lng > 256:
  331.             if usepbar:
  332.                 pbar.update(pbar.maxval-lng)
  333.             else:
  334.                 mdebug(5, "Read %(len)d bytes at 0x%(addr)X" % {'addr': addr, 'len': 256})
  335.             data = data + self.cmdReadMemory(addr, 256)
  336.             addr = addr + 256
  337.             lng = lng - 256
  338.         if usepbar:
  339.             pbar.update(pbar.maxval-lng)
  340.             pbar.finish()
  341.         else:
  342.             mdebug(5, "Read %(len)d bytes at 0x%(addr)X" % {'addr': addr, 'len': 256})
  343.         data = data + self.cmdReadMemory(addr, lng)
  344.         return data
  345.  
  346.     def writeMemory(self, addr, data):
  347.         lng = len(data)
  348.  
  349.         mdebug(5, "Writing %(lng)d bytes to start address 0x%(addr)X" %
  350.                { 'lng': lng, 'addr': addr})
  351.  
  352.         if usepbar:
  353.             widgets = ['Writing: ', Percentage(),' ', ETA(), ' ', Bar()]
  354.             pbar = ProgressBar(widgets=widgets, maxval=lng, term_width=79).start()
  355.  
  356.         offs = 0
  357.         while lng > 256:
  358.             if usepbar:
  359.                 pbar.update(pbar.maxval-lng)
  360.             else:
  361.                 mdebug(5, "Write %(len)d bytes at 0x%(addr)X" % {'addr': addr, 'len': 256})
  362.             self.cmdWriteMemory(addr, data[offs:offs+256])
  363.             offs = offs + 256
  364.             addr = addr + 256
  365.             lng = lng - 256
  366.         if usepbar:
  367.             pbar.update(pbar.maxval-lng)
  368.             pbar.finish()
  369.         else:
  370.             mdebug(5, "Write %(len)d bytes at 0x%(addr)X" % {'addr': addr, 'len': 256})
  371.         self.cmdWriteMemory(addr, data[offs:offs+lng] + ([0xFF] * (256-lng)) )
  372.  
  373.     def PCLKHack(self):
  374.         RCC_CFGR = 0x40021004
  375.         mdebug(5, "Modifying PCLK speed at 0x%(addr)X" % {'addr': RCC_CFGR})
  376. #        reg = self.cmdReadMemory(RCC_CFGR, 4)
  377. #        reg[1] = (reg[1] & 0xF8) | 0x04
  378.         reg = [10, 60, 29, 0]
  379. #        self.cmdWriteMemory(RCC_CFGR, reg)
  380.         if self.cmdGeneric(0x31):
  381.           self.sp.write(self._encode_addr(RCC_CFGR))
  382.           self._wait_for_ack("0x31 address failed")
  383.           self.sp.write(chr(3)) # len really
  384.           self.sp.write(chr(reg[0]))
  385.           self.sp.write(chr(reg[1]))
  386.           self.sp.write(chr(reg[2]))
  387.           self.sp.write(chr(reg[3]))            
  388.           crc = 3^reg[0]^reg[1]^reg[2]^reg[3];
  389.           self.sp.write(chr(crc))
  390.           self._wait_for_ack("0x31 programming failed")
  391.           mdebug(10, "    PCLK write memory done")
  392.  
  393.     def resetDevice(self):
  394.         AIRCR = 0xE000ED0C
  395.         mdebug(5, "Writing to Reset Register")
  396.         reg = [0x04,0x00,0xFA,0x05]
  397.         if self.cmdGeneric(0x31):
  398.           self.sp.write(self._encode_addr(AIRCR))
  399.           self._wait_for_ack("0x31 address failed")
  400.           self.sp.write(chr(3)) # len really
  401.           self.sp.write(chr(reg[0]))
  402.           self.sp.write(chr(reg[1]))
  403.           self.sp.write(chr(reg[2]))
  404.           self.sp.write(chr(reg[3]))            
  405.           crc = 3^reg[0]^reg[1]^reg[2]^reg[3];
  406.           self.sp.write(chr(crc))
  407.           # don't wait for ack - device will have rebooted
  408.           mdebug(10, "    reset done")
  409.  
  410.  
  411. def usage():
  412.     print("""Usage: %s [-hqVewvrX] [-l length] [-p port] [-b baud] [-a addr] [file.bin]
  413.    -h          This help
  414.    -q          Quiet
  415.    -V          Verbose
  416.    -e          Erase
  417.    -w          Write
  418.    -v          Verify
  419.    -X          Reset after
  420.    -r          Read
  421.    -l length   Length of read
  422.    -p port     Serial port (default: first USB-like port in /dev)
  423.    -b baud     Baud speed (default: 115200)
  424.    -a addr     Target address
  425.    -s n        Skip writing N bytes from beginning of the binary (does not affect start address)
  426.    -k          Change PCLK frequency to make USB stable on Espruino 1v43 bootloaders
  427.  
  428.    ./stm32loader.py -e -w -v example/main.bin
  429.  
  430.    """ % sys.argv[0])
  431.  
  432. def read(filename):
  433.     """Read the file to be programmed and turn it into a binary"""
  434.     with open(filename, 'rb') as f:
  435.         bytes = f.read()
  436.  
  437.     if bytes.startswith('\x7FELF'):
  438.         # Actually an ELF file.  Convert to binary
  439.         handle, path = tempfile.mkstemp(suffix='.bin', prefix='stm32loader')
  440.  
  441.         try:
  442.             os.close(handle)
  443.  
  444.             # Try a couple of options for objcopy
  445.             for name in ['arm-none-eabi-objcopy', 'arm-linux-gnueabi-objcopy']:
  446.                 try:
  447.                     code = subprocess.call([name, '-Obinary', filename, path])
  448.  
  449.                     if code == 0:
  450.                         return read(path)
  451.                 except OSError:
  452.                     pass
  453.             else:
  454.                 raise Exception('Error %d while converting to a binary file' % code)
  455.         finally:
  456.             # Remove the temporary file
  457.             os.unlink(path)
  458.     else:
  459.         return [ord(x) for x in bytes]
  460.  
  461. if __name__ == "__main__":
  462.  
  463.     had_error = False
  464.  
  465.     conf = {
  466.             'port': 'auto',
  467.             'baud': 115200,
  468.             'address': 0x08000000,
  469.             'skip' : 0,
  470.             'erase': 0,
  471.             'write': 0,
  472.             'verify': 0,
  473.             'read': 0,
  474.             'reset': 0,
  475.             'len': 1000,
  476.             'fname':'',
  477.             'pclk_hack':0,
  478.         }
  479.  
  480. # http://www.python.org/doc/2.5.2/lib/module-getopt.html
  481.  
  482.     try:
  483.         opts, args = getopt.getopt(sys.argv[1:], "hqVewvrXp:b:a:s:l:k")
  484.     except getopt.GetoptError as err:
  485.         # print help information and exit:
  486.         print(str(err)) # will print something like "option -a not recognized"
  487.         usage()
  488.         sys.exit(2)
  489.  
  490.     for o, a in opts:
  491.         if o == '-V':
  492.             QUIET = 10
  493.         elif o == '-q':
  494.             QUIET = 0
  495.         elif o == '-h':
  496.             usage()
  497.             sys.exit(0)
  498.         elif o == '-e':
  499.             conf['erase'] = 1
  500.         elif o == '-w':
  501.             conf['write'] = 1
  502.         elif o == '-v':
  503.             conf['verify'] = 1
  504.         elif o == '-r':
  505.             conf['read'] = 1
  506.         elif o == '-X':
  507.             conf['reset'] = 1
  508.         elif o == '-p':
  509.             conf['port'] = a
  510.         elif o == '-b':
  511.             conf['baud'] = eval(a)
  512.         elif o == '-a':
  513.             conf['address'] = eval(a)
  514.         elif o == '-s':
  515.             conf['skip'] = eval(a)
  516.         elif o == '-l':
  517.             conf['len'] = eval(a)
  518.         elif o == '-k':
  519.             conf['pclk_hack'] = 1
  520.         else:
  521.             assert False, "unhandled option"
  522.     # Try and find the port automatically
  523.     if conf['port'] == 'auto':
  524.         ports = []
  525.  
  526.         # Get a list of all USB-like names in /dev
  527.         for name in ['tty.usbserial', 'ttyUSB']:
  528.             ports.extend(glob.glob('/dev/%s*' % name))
  529.  
  530.         ports = sorted(ports)
  531.  
  532.         if ports:
  533.             # Found something - take it
  534.             conf['port'] = ports[0]
  535.  
  536.     cmd = CommandInterface()
  537.     cmd.open(conf['port'], conf['baud'])
  538.     mdebug(10, "Open port %(port)s, baud %(baud)d" % {'port':conf['port'],
  539.                                                       'baud':conf['baud']})
  540.     try:
  541.         if (conf['write'] or conf['verify']):
  542.             mdebug(5, "Reading data from %s" % args[0])
  543.             data = read(args[0])
  544.  
  545.             if conf['skip']:
  546.                 mdebug(5, "Skipping %d bytes" % conf['skip'])
  547.                 data = data[conf['skip']:]
  548.  
  549.         try:
  550.             cmd.initChip()
  551.         except CmdException:
  552.             print("Can't init. Ensure BOOT0=1, BOOT1=0, and reset device")
  553.  
  554.         bootversion = cmd.cmdGet()
  555.  
  556.         mdebug(0, "Bootloader version 0x%X" % bootversion)
  557.  
  558.         if bootversion < 20 or bootversion >= 100:
  559.             raise Exception('Unreasonable bootloader version %d' % bootversion)
  560.  
  561.         chip_id = cmd.cmdGetID()
  562.         assert len(chip_id) == 2, "Unreasonable chip id: %s" % repr(chip_id)
  563.         chip_id_num = (ord(chip_id[0]) << 8) | ord(chip_id[1])
  564.         chip_id_str = CHIP_ID_STRS.get(chip_id_num, None)
  565.  
  566.         if chip_id_str is None:
  567.             mdebug(0, 'Warning: unrecognised chip ID 0x%x' % chip_id_num)
  568.         else:
  569.             mdebug(0, "Chip id 0x%x, %s" % (chip_id_num, chip_id_str))
  570.  
  571.         if conf['pclk_hack']:
  572.             cmd.PCLKHack()
  573.  
  574.         if conf['erase']:
  575.             # Pre-3.0 bootloaders use the erase memory
  576.             # command. Starting with 3.0, extended erase memory
  577.             # replaced this command.
  578.             if bootversion < 0x30:
  579.                 cmd.cmdEraseMemory()
  580.             else:
  581.                 cmd.cmdExtendedEraseMemory()
  582.  
  583.         if conf['write']:
  584.             cmd.writeMemory(conf['address'], data)
  585.  
  586.         if conf['verify']:
  587.             verify = cmd.readMemory(conf['address'], len(data))
  588.             if(data == verify):
  589.                 print("Verification OK")
  590.             else:
  591.                 print("Verification FAILED")
  592.                 print(str(len(data)) + ' vs ' + str(len(verify)))
  593.                 for i in xrange(0, len(data)):
  594.                     if data[i] != verify[i]:
  595.                         print(hex(i) + ': ' + hex(data[i]) + ' vs ' + hex(verify[i]))
  596.                 had_error = True
  597.  
  598.         if not conf['write'] and conf['read']:
  599.             rdata = cmd.readMemory(conf['address'], conf['len'])
  600.             file(args[0], 'wb').write(''.join(map(chr,rdata)))
  601.  
  602.         if conf['reset']:
  603.             cmd.resetDevice()
  604.  
  605.     finally:
  606.         if not conf['reset']:
  607.             cmd.releaseChip()
  608.  
  609.     if had_error: exit(1)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement