dfu.py 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. # Copyright (c) 2017 Linaro Limited.
  2. #
  3. # SPDX-License-Identifier: Apache-2.0
  4. '''Runner for flashing with dfu-util.'''
  5. from collections import namedtuple
  6. import sys
  7. import time
  8. from runners.core import ZephyrBinaryRunner, RunnerCaps, \
  9. BuildConfiguration
  10. DfuSeConfig = namedtuple('DfuSeConfig', ['address', 'options'])
  11. class DfuUtilBinaryRunner(ZephyrBinaryRunner):
  12. '''Runner front-end for dfu-util.'''
  13. def __init__(self, cfg, pid, alt, img, exe='dfu-util',
  14. dfuse_config=None):
  15. super().__init__(cfg)
  16. self.alt = alt
  17. self.img = img
  18. self.cmd = [exe, '-d,{}'.format(pid)]
  19. try:
  20. self.list_pattern = ', alt={},'.format(int(self.alt))
  21. except ValueError:
  22. self.list_pattern = ', name="{}",'.format(self.alt)
  23. if dfuse_config is None:
  24. self.dfuse = False
  25. else:
  26. self.dfuse = True
  27. self.dfuse_config = dfuse_config
  28. self.reset = False
  29. @classmethod
  30. def name(cls):
  31. return 'dfu-util'
  32. @classmethod
  33. def capabilities(cls):
  34. return RunnerCaps(commands={'flash'}, flash_addr=True)
  35. @classmethod
  36. def do_add_parser(cls, parser):
  37. # Required:
  38. parser.add_argument("--pid", required=True,
  39. help="USB VID:PID of the board")
  40. parser.add_argument("--alt", required=True,
  41. help="interface alternate setting number or name")
  42. # Optional:
  43. parser.add_argument("--img",
  44. help="binary to flash, default is --bin-file")
  45. parser.add_argument("--dfuse", default=False, action='store_true',
  46. help='''use the DfuSe protocol extensions
  47. supported by STMicroelectronics
  48. devices (if given, the image flash
  49. address respects
  50. CONFIG_FLASH_BASE_ADDRESS and
  51. CONFIG_FLASH_LOAD_OFFSET)''')
  52. parser.add_argument("--dfuse-modifiers", default='leave',
  53. help='''colon-separated list of additional
  54. DfuSe modifiers for dfu-util's -s
  55. option (default is
  56. "-s <flash-address>:leave", which starts
  57. execution immediately); requires
  58. --dfuse
  59. ''')
  60. parser.add_argument('--dfu-util', default='dfu-util',
  61. help='dfu-util executable; defaults to "dfu-util"')
  62. @classmethod
  63. def do_create(cls, cfg, args):
  64. if args.img is None:
  65. args.img = cfg.bin_file
  66. if args.dfuse:
  67. args.dt_flash = True # --dfuse implies --dt-flash.
  68. build_conf = BuildConfiguration(cfg.build_dir)
  69. dcfg = DfuSeConfig(address=cls.get_flash_address(args, build_conf),
  70. options=args.dfuse_modifiers)
  71. else:
  72. dcfg = None
  73. ret = DfuUtilBinaryRunner(cfg, args.pid, args.alt, args.img,
  74. exe=args.dfu_util, dfuse_config=dcfg)
  75. ret.ensure_device()
  76. return ret
  77. def ensure_device(self):
  78. if not self.find_device():
  79. self.reset = True
  80. print('Please reset your board to switch to DFU mode...')
  81. while not self.find_device():
  82. time.sleep(0.1)
  83. def find_device(self):
  84. cmd = list(self.cmd) + ['-l']
  85. output = self.check_output(cmd)
  86. output = output.decode(sys.getdefaultencoding())
  87. return self.list_pattern in output
  88. def do_run(self, command, **kwargs):
  89. self.require(self.cmd[0])
  90. self.ensure_output('bin')
  91. if not self.find_device():
  92. raise RuntimeError('device not found')
  93. cmd = list(self.cmd)
  94. if self.dfuse:
  95. # http://dfu-util.sourceforge.net/dfuse.html
  96. dcfg = self.dfuse_config
  97. addr_opts = hex(dcfg.address) + ':' + dcfg.options
  98. cmd.extend(['-s', addr_opts])
  99. cmd.extend(['-a', self.alt, '-D', self.img])
  100. self.check_call(cmd)
  101. if self.dfuse and 'leave' in dcfg.options.split(':'):
  102. # Normal DFU devices generally need to be reset to switch
  103. # back to the flashed program.
  104. #
  105. # DfuSe targets do as well, except when 'leave' is given
  106. # as an option.
  107. self.reset = False
  108. if self.reset:
  109. print('Now reset your board again to switch back to runtime mode.')