Merge branch 'feature/dfu_select_device' into 'master'

Add ability to select a device for DFU flashing

Closes IDF-1652

See merge request espressif/esp-idf!8956
This commit is contained in:
Ivan Grokhotkov 2020-06-08 15:55:04 +08:00
commit 7283b99c97
7 changed files with 88 additions and 11 deletions

View File

@ -64,6 +64,22 @@ which relies on `dfu-util <http://dfu-util.sourceforge.net/>`_. Please see :ref:
installing ``dfu-util``. ``dfu-util`` needs additional setup for :ref:`api_guide_dfu_flash_win` or setting up an installing ``dfu-util``. ``dfu-util`` needs additional setup for :ref:`api_guide_dfu_flash_win` or setting up an
:ref:`api_guide_dfu_flash_udev`. Mac OS users should be able to use ``dfu-util`` without further setup. :ref:`api_guide_dfu_flash_udev`. Mac OS users should be able to use ``dfu-util`` without further setup.
If there are more boards with the same chip connected then ``idf.py dfu-list`` can be used to list the available
devices, for example::
Found Runtime: [303a:0002] ver=0723, devnum=4, cfg=1, intf=2, path="1-10", alt=0, name="UNKNOWN", serial="0"
Found Runtime: [303a:0002] ver=0723, devnum=6, cfg=1, intf=2, path="1-2", alt=0, name="UNKNOWN", serial="0"
Consequently, the desired device can be selected for flashing by the ``--path`` argument. For example, the devices
listed above can be flashed individually by the following commands::
idf.py dfu-flash --path 1-10
idf.py dfu-flash --path 1-2
.. note::
The vendor and product identificators are set based on the selected chip target by the ``idf.py set-target``
command and it is not selectable during the ``idf.py dfu-flash`` call.
See :ref:`api_guide_dfu_flash_errors` and their solutions. See :ref:`api_guide_dfu_flash_errors` and their solutions.
.. _api_guide_dfu_flash_udev: .. _api_guide_dfu_flash_udev:

View File

@ -3,8 +3,12 @@
function(__add_dfu_targets) function(__add_dfu_targets)
idf_build_get_property(target IDF_TARGET) idf_build_get_property(target IDF_TARGET)
if(NOT "${target}" STREQUAL "esp32s2") if("${target}" STREQUAL "esp32")
return() return()
elseif("${target}" STREQUAL "esp32s2")
set(dfu_pid "2")
else()
message(FATAL_ERROR "DFU PID unknown for ${target}")
endif() endif()
idf_build_get_property(python PYTHON) idf_build_get_property(python PYTHON)
@ -14,13 +18,21 @@ function(__add_dfu_targets)
COMMAND ${python} ${idf_path}/tools/mkdfu.py write COMMAND ${python} ${idf_path}/tools/mkdfu.py write
-o "${CMAKE_CURRENT_BINARY_DIR}/dfu.bin" -o "${CMAKE_CURRENT_BINARY_DIR}/dfu.bin"
--json "${CMAKE_CURRENT_BINARY_DIR}/flasher_args.json" --json "${CMAKE_CURRENT_BINARY_DIR}/flasher_args.json"
--pid "${dfu_pid}"
DEPENDS gen_project_binary bootloader DEPENDS gen_project_binary bootloader
VERBATIM VERBATIM
USES_TERMINAL) USES_TERMINAL)
add_custom_target(dfu-list
COMMAND ${CMAKE_COMMAND}
-D ESP_DFU_LIST="1"
-P ${idf_path}/tools/cmake/run_dfu_util.cmake
USES_TERMINAL)
add_custom_target(dfu-flash add_custom_target(dfu-flash
COMMAND dfu-util COMMAND ${CMAKE_COMMAND}
-D "${CMAKE_CURRENT_BINARY_DIR}/dfu.bin" -D ESP_DFU_BIN="${CMAKE_CURRENT_BINARY_DIR}/dfu.bin"
VERBATIM -D ESP_DFU_PID="${dfu_pid}"
-P ${idf_path}/tools/cmake/run_dfu_util.cmake
USES_TERMINAL) USES_TERMINAL)
endfunction() endfunction()

View File

@ -0,0 +1,28 @@
# A CMake script to run dfu-util from within ninja or make
# or another cmake-based build runner
#
# It is recommended to NOT USE this CMake script directly
cmake_minimum_required(VERSION 3.5)
set(TOOL "dfu-util")
set(CMD "${TOOL}")
if(${ESP_DFU_LIST})
list(APPEND CMD "--list")
else()
# The following works even when ESP_DFU_PID is not defined.
list(APPEND CMD "-d" "303a:${ESP_DFU_PID}")
if(NOT $ENV{ESP_DFU_PATH} STREQUAL "")
list(APPEND CMD "--path" $ENV{ESP_DFU_PATH})
endif()
list(APPEND CMD "-D" ${ESP_DFU_BIN})
endif()
message("Command list: ${CMD}")
execute_process(COMMAND ${CMD} RESULT_VARIABLE result)
if(${result})
message(FATAL_ERROR "${TOOL} failed")
endif()

View File

@ -10,11 +10,11 @@ def action_extensions(base_actions, project_path):
ensure_build_directory(args, ctx.info_name) ensure_build_directory(args, ctx.info_name)
run_target(target_name, args) run_target(target_name, args)
def dfu_flash_target(target_name, ctx, args): def dfu_flash_target(target_name, ctx, args, path):
ensure_build_directory(args, ctx.info_name) ensure_build_directory(args, ctx.info_name)
try: try:
run_target(target_name, args) run_target(target_name, args, {"ESP_DFU_PATH": path})
except FatalError: except FatalError:
# Cannot capture the error from dfu-util here so the best advise is: # Cannot capture the error from dfu-util here so the best advise is:
print('Please have a look at the "Device Firmware Upgrade through USB" chapter in API Guides of the ' print('Please have a look at the "Device Firmware Upgrade through USB" chapter in API Guides of the '
@ -28,10 +28,24 @@ def action_extensions(base_actions, project_path):
"short_help": "Build the DFU binary", "short_help": "Build the DFU binary",
"dependencies": ["all"], "dependencies": ["all"],
}, },
"dfu-list": {
"callback": dfu_target,
"short_help": "List DFU capable devices",
"dependencies": [],
},
"dfu-flash": { "dfu-flash": {
"callback": dfu_flash_target, "callback": dfu_flash_target,
"short_help": "Flash the DFU binary", "short_help": "Flash the DFU binary",
"order_dependencies": ["dfu"], "order_dependencies": ["dfu"],
"options": [
{
"names": ["--path"],
"default": "",
"help": "Specify path to DFU device. The default empty path works if there is just one "
"ESP device with the same product identificator. See the device list for paths "
"of available devices."
}
],
}, },
} }
} }

View File

@ -107,8 +107,6 @@ DFUSuffix = namedtuple(
"DFUSuffix", ["bcd_device", "pid", "vid", "bcd_dfu", "sig", "len"] "DFUSuffix", ["bcd_device", "pid", "vid", "bcd_dfu", "sig", "len"]
) )
ESPRESSIF_VID = 12346 ESPRESSIF_VID = 12346
# TODO: set PID based on the chip type (add a command line argument)
DFUSUFFIX_DEFAULT = DFUSuffix(0xFFFF, 0xFFFF, ESPRESSIF_VID, 0x0100, b"UFD", 16)
# This CRC32 gets added after DFUSUFFIX_STRUCT # This CRC32 gets added after DFUSUFFIX_STRUCT
DFUCRC_STRUCT = b"<I" DFUCRC_STRUCT = b"<I"
@ -126,8 +124,9 @@ def pad_bytes(b, multiple, padding=b"\x00"): # type: (bytes, int, bytes) -> byt
class EspDfuWriter(object): class EspDfuWriter(object):
def __init__(self, dest_file): # type: (typing.BinaryIO) -> None def __init__(self, dest_file, pid): # type: (typing.BinaryIO) -> None
self.dest = dest_file self.dest = dest_file
self.pid = pid
self.entries = [] # type: typing.List[bytes] self.entries = [] # type: typing.List[bytes]
self.index = [] # type: typing.List[DFUInfo] self.index = [] # type: typing.List[DFUInfo]
@ -151,7 +150,8 @@ class EspDfuWriter(object):
out_data = pad_bytes(out_data, cpio_block_size) out_data = pad_bytes(out_data, cpio_block_size)
# Add DFU suffix and CRC # Add DFU suffix and CRC
out_data += struct.pack(DFUSUFFIX_STRUCT, *DFUSUFFIX_DEFAULT) dfu_suffix = DFUSuffix(0xFFFF, self.pid, ESPRESSIF_VID, 0x0100, b"UFD", 16)
out_data += struct.pack(DFUSUFFIX_STRUCT, *dfu_suffix)
out_data += struct.pack(DFUCRC_STRUCT, dfu_crc(out_data)) out_data += struct.pack(DFUCRC_STRUCT, dfu_crc(out_data))
# Finally write the entire binary # Finally write the entire binary
@ -187,7 +187,7 @@ class EspDfuWriter(object):
def action_write(args): def action_write(args):
writer = EspDfuWriter(args['output_file']) writer = EspDfuWriter(args['output_file'], args['pid'])
for addr, f in args['files']: for addr, f in args['files']:
print('Adding {} at {:#x}'.format(f, addr)) print('Adding {} at {:#x}'.format(f, addr))
writer.add_file(addr, f) writer.add_file(addr, f)
@ -205,6 +205,10 @@ def main():
help='Filename for storing the output DFU image', help='Filename for storing the output DFU image',
required=True, required=True,
type=argparse.FileType("wb")) type=argparse.FileType("wb"))
write_parser.add_argument("--pid",
required=True,
type=lambda h: int(h, 16),
help='Hexa-decimal product indentificator')
write_parser.add_argument("--json", write_parser.add_argument("--json",
help='Optional file for loading "flash_files" dictionary with <address> <file> items') help='Optional file for loading "flash_files" dictionary with <address> <file> items')
write_parser.add_argument("files", write_parser.add_argument("files",
@ -241,6 +245,7 @@ def main():
cmd_args = {'output_file': args.output_file, cmd_args = {'output_file': args.output_file,
'files': files, 'files': files,
'pid': args.pid,
} }
{'write': action_write {'write': action_write

Binary file not shown.

View File

@ -35,6 +35,7 @@ class TestHelloWorldExample(unittest.TestCase):
self.addCleanup(os.unlink, f.name) self.addCleanup(os.unlink, f.name)
cmd = ' '.join([sys.executable, mkdfu_path, 'write', cmd = ' '.join([sys.executable, mkdfu_path, 'write',
'-o', f.name, '-o', f.name,
'--pid', '2',
add_args]) add_args])
p = pexpect.spawn(cmd, timeout=10) p = pexpect.spawn(cmd, timeout=10)
self.addCleanup(p.terminate, force=True) self.addCleanup(p.terminate, force=True)
@ -81,6 +82,7 @@ class TestHelloWorldExample(unittest.TestCase):
cmd = ' '.join([sys.executable, mkdfu_path, 'write', cmd = ' '.join([sys.executable, mkdfu_path, 'write',
'-o', output, '-o', output,
'--pid', '2',
' '.join(['0x1000', bootloader, ' '.join(['0x1000', bootloader,
'0x8000', os.path.join(current_dir, '1', '2.bin'), '0x8000', os.path.join(current_dir, '1', '2.bin'),
'0x10000', os.path.join(current_dir, '1', '3.bin') '0x10000', os.path.join(current_dir, '1', '3.bin')