diff --git a/docs/conf_common.py b/docs/conf_common.py
index 36ecf4ff75..e7ad6e2dd2 100644
--- a/docs/conf_common.py
+++ b/docs/conf_common.py
@@ -159,6 +159,7 @@ def update_exclude_patterns(tags):
# note: in toctrees, these also need to be marked with a :esp32: filter
for e in ['esp32s2.rst',
'hw-reference/esp32s2/**',
+ 'api-guides/dfu.rst',
'api-guides/ulps2_instruction_set.rst',
'api-reference/peripherals/hmac.rst',
'api-reference/peripherals/temp_sensor.rst']:
diff --git a/docs/en/api-guides/dfu.rst b/docs/en/api-guides/dfu.rst
new file mode 100644
index 0000000000..251b58c5c0
--- /dev/null
+++ b/docs/en/api-guides/dfu.rst
@@ -0,0 +1,99 @@
+***********************************************
+Device Firmware Upgrade through USB
+***********************************************
+
+.. only:: esp32
+
+ .. note::
+ Device Firmware Upgrade through USB is not supported with ESP32 chips.
+
+Device Firmware Upgrade (DFU) is a mechanism for upgrading the firmware of devices through Universal Serial Bus (USB).
+DFU is supported by ESP32-S2 chips. The necessary connections for the USB peripheral are shown in the following table.
+
++------+-------------+
+| GPIO | USB |
++======+=============+
+| 19 | D- (green) |
++------+-------------+
+| 20 | D+ (white) |
++------+-------------+
+| GND | GND (black) |
++------+-------------+
+| | +5V (red) |
++------+-------------+
+
+The software requirements of DFU are included in :ref:`get-started-get-prerequisites` of the Getting Started Guide.
+
+Section :ref:`api_guide_dfu_build` describes how to build firmware for DFU with ESP-IDF and
+Section :ref:`api_guide_dfu_flash` deals with flashing the firmware.
+
+.. _api_guide_dfu_build:
+
+Building the DFU Image
+======================
+
+The DFU image can be created by running::
+
+ idf.py dfu
+
+which creates ``dfu.bin`` in the build directory.
+
+.. note::
+ Don't forget to set the target chip by ``idf.py set-target`` before running ``idf.py dfu``. Otherwise, you might
+ create an image for a different chip or receive an error message something like ``unknown target 'dfu'``.
+
+.. _api_guide_dfu_flash:
+
+Flashing the Chip with the DFU Image
+====================================
+
+The DFU image is downloaded into the chip by running::
+
+ idf.py dfu-flash
+
+which relies on `dfu-util `_. Please see :ref:`get-started-get-prerequisites` for
+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.
+
+See :ref:`api_guide_dfu_flash_errors` and their solutions.
+
+.. _api_guide_dfu_flash_udev:
+
+udev rule (Linux only)
+----------------------
+
+udev is a device manager for the Linux kernel. It allows us to run ``dfu-util`` (and ``idf.py dfu-flash``) without
+``sudo`` for gaining access to the chip.
+
+Create file ``/etc/udev/rules.d/40-dfuse.rules`` with the following content::
+
+ SUBSYSTEMS=="usb", ATTRS{idVendor}=="303a", ATTRS{idProduct}=="00??", GROUP="plugdev", MODE="0666"
+
+.. note::
+ Please check the output of command ``groups``. The user has to be a member of the `GROUP` specified above. You may
+ use some other existing group for this purpose (e.g. `uucp` on some systems instead of `plugdev`) or create a new
+ group for this purpose.
+
+Restart your computer so the previous setting could take into affect or run ``sudo udevadm trigger`` to force
+manually udev to trigger your new rule.
+
+.. _api_guide_dfu_flash_win:
+
+USB drivers (Windows only)
+--------------------------
+
+``dfu-util`` uses `libusb` to access the device. You have to register on Windows the device with the `WinUSB` driver.
+Please see the `libusb wiki `_ for more
+details.
+
+.. _api_guide_dfu_flash_errors:
+
+Common errors
+-------------
+
+- ``dfu-util: command not found`` might indicate that the tool hasn't been installed or is not available from the terminal.
+ An easy way of checking the tool is running ``dfu-util --version``. Please see :ref:`get-started-get-prerequisites` for
+ installing ``dfu-util``.
+- The reason for ``No DFU capable USB device available`` could be that the USB driver wasn't properly installed on
+ Windows (see :ref:`api_guide_dfu_flash_win`) or udev rule was not setup on Linux
+ (see :ref:`api_guide_dfu_flash_udev`).
diff --git a/docs/en/api-guides/index.rst b/docs/en/api-guides/index.rst
index 4b11122acd..946b85f6c5 100644
--- a/docs/en/api-guides/index.rst
+++ b/docs/en/api-guides/index.rst
@@ -11,6 +11,7 @@ API Guides
Build System
:esp32: Build System (Legacy GNU Make)
Deep Sleep Wake Stubs
+ :esp32s2: Device Firmware Upgrade through USB
Error Handling
:esp32: ESP-BLE-MESH
ESP-MESH (Wi-Fi)
diff --git a/docs/en/api-guides/tools/idf-tools-notes.inc b/docs/en/api-guides/tools/idf-tools-notes.inc
index ffd58cd364..426b33edbc 100644
--- a/docs/en/api-guides/tools/idf-tools-notes.inc
+++ b/docs/en/api-guides/tools/idf-tools-notes.inc
@@ -47,4 +47,9 @@ On Linux and macOS, it is recommended to install ninja using the OS-specific pac
.. tool-ccache-notes
+---
+
+.. tool-dfu-util-notes
+
+
---
diff --git a/docs/en/get-started/linux-setup-scratch.rst b/docs/en/get-started/linux-setup-scratch.rst
index 1ba2f560df..07b236c9ac 100644
--- a/docs/en/get-started/linux-setup-scratch.rst
+++ b/docs/en/get-started/linux-setup-scratch.rst
@@ -15,15 +15,15 @@ To compile with ESP-IDF you need to get the following packages:
- CentOS 7::
- sudo yum install git wget ncurses-devel flex bison gperf python pyserial python-pyelftools cmake ninja-build ccache
+ sudo yum install git wget ncurses-devel flex bison gperf python pyserial python-pyelftools cmake ninja-build ccache dfu-util
- Ubuntu and Debian::
- sudo apt-get install git wget libncurses-dev flex bison gperf python python-pip python-setuptools python-serial python-click python-cryptography python-future python-pyparsing python-pyelftools cmake ninja-build ccache libffi-dev libssl-dev
+ sudo apt-get install git wget libncurses-dev flex bison gperf python python-pip python-setuptools python-serial python-click python-cryptography python-future python-pyparsing python-pyelftools cmake ninja-build ccache libffi-dev libssl-dev dfu-util
- Arch::
- sudo pacman -S --needed gcc git make ncurses flex bison gperf python-pyserial python-click python-cryptography python-future python-pyparsing python-pyelftools cmake ninja ccache
+ sudo pacman -S --needed gcc git make ncurses flex bison gperf python-pyserial python-click python-cryptography python-future python-pyparsing python-pyelftools cmake ninja ccache dfu-util
.. note::
CMake version 3.5 or newer is required for use with ESP-IDF. Older Linux distributions may require updating, enabling of a "backports" repository, or installing of a "cmake3" package rather than "cmake".
diff --git a/docs/en/get-started/linux-setup.rst b/docs/en/get-started/linux-setup.rst
index bde40fe2af..80fcc75b1d 100644
--- a/docs/en/get-started/linux-setup.rst
+++ b/docs/en/get-started/linux-setup.rst
@@ -11,15 +11,15 @@ To compile with ESP-IDF you need to get the following packages:
- CentOS 7::
- sudo yum install git wget flex bison gperf python cmake ninja-build ccache
+ sudo yum install git wget flex bison gperf python cmake ninja-build ccache dfu-util
- Ubuntu and Debian::
- sudo apt-get install git wget flex bison gperf python python-pip python-setuptools cmake ninja-build ccache libffi-dev libssl-dev
+ sudo apt-get install git wget flex bison gperf python python-pip python-setuptools cmake ninja-build ccache libffi-dev libssl-dev dfu-util
- Arch::
- sudo pacman -S --needed gcc git make flex bison gperf python-pip cmake ninja ccache
+ sudo pacman -S --needed gcc git make flex bison gperf python-pip cmake ninja ccache dfu-util
.. note::
CMake version 3.5 or newer is required for use with ESP-IDF. Older Linux distributions may require updating, enabling of a "backports" repository, or installing of a "cmake3" package rather than "cmake".
diff --git a/docs/en/get-started/macos-setup-scratch.rst b/docs/en/get-started/macos-setup-scratch.rst
index e906b9fe78..1223730384 100644
--- a/docs/en/get-started/macos-setup-scratch.rst
+++ b/docs/en/get-started/macos-setup-scratch.rst
@@ -31,11 +31,11 @@ Install Prerequisites
- If you have HomeBrew, you can run::
- brew install cmake ninja
+ brew install cmake ninja dfu-util
- If you have MacPorts, you can run::
- sudo port install cmake ninja
+ sudo port install cmake ninja dfu-util
Compile the Toolchain from Source
=================================
diff --git a/docs/en/get-started/macos-setup.rst b/docs/en/get-started/macos-setup.rst
index caede42ece..5a072c3d73 100644
--- a/docs/en/get-started/macos-setup.rst
+++ b/docs/en/get-started/macos-setup.rst
@@ -21,11 +21,11 @@ ESP-IDF will use the version of Python installed by default on macOS.
- If you have HomeBrew_, you can run::
- brew install cmake ninja
+ brew install cmake ninja dfu-util
- If you have MacPorts_, you can run::
- sudo port install cmake ninja
+ sudo port install cmake ninja dfu-util
- Otherwise, consult the CMake_ and Ninja_ home pages for macOS installation downloads.
diff --git a/docs/zh_CN/api-guides/dfu.rst b/docs/zh_CN/api-guides/dfu.rst
new file mode 100644
index 0000000000..566cdee390
--- /dev/null
+++ b/docs/zh_CN/api-guides/dfu.rst
@@ -0,0 +1 @@
+.. include:: ../../en/api-guides/dfu.rst
diff --git a/docs/zh_CN/api-guides/index.rst b/docs/zh_CN/api-guides/index.rst
index 09a32d945d..9a08e2ad3c 100644
--- a/docs/zh_CN/api-guides/index.rst
+++ b/docs/zh_CN/api-guides/index.rst
@@ -12,6 +12,7 @@ API 指南
严重错误
Event Handling
Deep Sleep Wake Stubs
+ :esp32s2: Device Firmware Upgrade through USB
ESP32 Core Dump
Flash Encryption <../security/flash-encryption>
FreeRTOS SMP Changes
diff --git a/docs/zh_CN/api-guides/tools/idf-tools-notes.inc b/docs/zh_CN/api-guides/tools/idf-tools-notes.inc
index c7c750089e..946e8222cd 100644
--- a/docs/zh_CN/api-guides/tools/idf-tools-notes.inc
+++ b/docs/zh_CN/api-guides/tools/idf-tools-notes.inc
@@ -49,4 +49,9 @@ On Linux and macOS, it is recommended to install ninja using the OS package mana
.. tool-ccache-notes
+---
+
+.. tool-dfu-util-notes
+
+
---
diff --git a/docs/zh_CN/get-started/linux-setup-scratch.rst b/docs/zh_CN/get-started/linux-setup-scratch.rst
index 3d8bee637e..5964e4c3ca 100644
--- a/docs/zh_CN/get-started/linux-setup-scratch.rst
+++ b/docs/zh_CN/get-started/linux-setup-scratch.rst
@@ -13,15 +13,15 @@
- CentOS 7::
- sudo yum install git wget ncurses-devel flex bison gperf python pyserial python-pyelftools cmake ninja-build ccache
+ sudo yum install git wget ncurses-devel flex bison gperf python pyserial python-pyelftools cmake ninja-build ccache dfu-util
- Ubuntu 和 Debian::
- sudo apt-get install git wget libncurses-dev flex bison gperf python python-pip python-setuptools python-serial python-click python-cryptography python-future python-pyparsing python-pyelftools cmake ninja-build ccache libffi-dev libssl-dev
+ sudo apt-get install git wget libncurses-dev flex bison gperf python python-pip python-setuptools python-serial python-click python-cryptography python-future python-pyparsing python-pyelftools cmake ninja-build ccache libffi-dev libssl-dev dfu-util
- Arch::
- sudo pacman -S --needed gcc git make ncurses flex bison gperf python-pyserial python-click python-cryptography python-future python-pyparsing python-pyelftools cmake ninja ccache
+ sudo pacman -S --needed gcc git make ncurses flex bison gperf python-pyserial python-click python-cryptography python-future python-pyparsing python-pyelftools cmake ninja ccache dfu-util
.. note::
使用 ESP-IDF 需要 CMake 3.5 或以上版本。较早版本的 Linux 可能需要升级才能向后移植仓库,或安装 "cmake3" 软件包,而不是安装 "cmake"。
diff --git a/docs/zh_CN/get-started/linux-setup.rst b/docs/zh_CN/get-started/linux-setup.rst
index 4f6019857d..38b04a8552 100644
--- a/docs/zh_CN/get-started/linux-setup.rst
+++ b/docs/zh_CN/get-started/linux-setup.rst
@@ -11,15 +11,15 @@ Linux 平台工具链的标准设置
- CentOS 7::
- sudo yum install git wget flex bison gperf python cmake ninja-build ccache
+ sudo yum install git wget flex bison gperf python cmake ninja-build ccache dfu-util
- Ubuntu 和 Debian::
- sudo apt-get install git wget flex bison gperf python python-pip python-setuptools cmake ninja-build ccache libffi-dev libssl-dev
+ sudo apt-get install git wget flex bison gperf python python-pip python-setuptools cmake ninja-build ccache libffi-dev libssl-dev dfu-util
- Arch::
- sudo pacman -S --needed gcc git make flex bison gperf python-pip python-pyserial cmake ninja ccache
+ sudo pacman -S --needed gcc git make flex bison gperf python-pip python-pyserial cmake ninja ccache dfu-util
.. note::
使用 ESP-IDF 需要 CMake 3.5 或以上版本。较早版本的 Linux 可能需要升级才能向后移植仓库,或安装 "cmake3" 软件包,而不是安装 "cmake"。
diff --git a/docs/zh_CN/get-started/macos-setup-scratch.rst b/docs/zh_CN/get-started/macos-setup-scratch.rst
index f880317cd9..68c0d5abab 100644
--- a/docs/zh_CN/get-started/macos-setup-scratch.rst
+++ b/docs/zh_CN/get-started/macos-setup-scratch.rst
@@ -31,11 +31,11 @@ MacPorts 需要完整的 XCode 软件,而 homebrew 只需要安装 XCode 命
- 若有 HomeBrew,您可以运行::
- brew install cmake ninja
+ brew install cmake ninja dfu-util
- 若有 MacPorts,您可以运行::
- sudo port install cmake ninja
+ sudo port install cmake ninja dfu-util
从源代码编译工具链
=================================
diff --git a/docs/zh_CN/get-started/macos-setup.rst b/docs/zh_CN/get-started/macos-setup.rst
index 4e9322550b..c5d3e52bd1 100644
--- a/docs/zh_CN/get-started/macos-setup.rst
+++ b/docs/zh_CN/get-started/macos-setup.rst
@@ -21,11 +21,11 @@ ESP-IDF 将使用 Mac OS 上默认安装的 Python 版本。
- 若有 HomeBrew_,您可以运行::
- brew install cmake ninja
+ brew install cmake ninja dfu-util
- 若有 MacPorts_,您可以运行::
- sudo port install cmake ninja
+ sudo port install cmake ninja dfu-util
- 若以上均不适用,请访问 CMake_ 和 Ninja_ 主页,查询有关 Mac OS 平台的下载安装问题。
diff --git a/tools/ci/config/host-test.yml b/tools/ci/config/host-test.yml
index 84ec1cee81..e7018ddc1b 100644
--- a/tools/ci/config/host-test.yml
+++ b/tools/ci/config/host-test.yml
@@ -304,3 +304,11 @@ test_sysviewtrace_proc:
script:
- cd ${IDF_PATH}/tools/esp_app_trace/test/sysview
- ${IDF_PATH}/tools/ci/multirun_with_pyenv.sh ./test.sh
+
+test_mkdfu:
+ extends: .host_test_template
+ variables:
+ LC_ALL: C.UTF-8
+ script:
+ - cd ${IDF_PATH}/tools/test_mkdfu
+ - ${IDF_PATH}/tools/ci/multirun_with_pyenv.sh ./test_mkdfu.py
diff --git a/tools/ci/executable-list.txt b/tools/ci/executable-list.txt
index 690ba367fe..426e308513 100644
--- a/tools/ci/executable-list.txt
+++ b/tools/ci/executable-list.txt
@@ -85,12 +85,14 @@ tools/ldgen/ldgen.py
tools/ldgen/test/test_fragments.py
tools/ldgen/test/test_generation.py
tools/mass_mfg/mfg_gen.py
+tools/mkdfu.py
tools/set-submodules-to-github.sh
tools/test_check_kconfigs.py
tools/test_idf_monitor/run_test_idf_monitor.py
tools/test_idf_py/test_idf_py.py
tools/test_idf_size/test.sh
tools/test_idf_tools/test_idf_tools.py
+tools/test_mkdfu/test_mkdfu.py
tools/unit-test-app/tools/get_available_configs.sh
tools/unit-test-app/unit_test.py
tools/windows/eclipse_make.sh
diff --git a/tools/ci/test_build_system_cmake.sh b/tools/ci/test_build_system_cmake.sh
index ad7e8e49ba..5552344681 100755
--- a/tools/ci/test_build_system_cmake.sh
+++ b/tools/ci/test_build_system_cmake.sh
@@ -700,6 +700,16 @@ endmenu\n" >> ${IDF_PATH}/Kconfig
bin_header_match build/bootloader/bootloader.bin "021f"
rm sdkconfig
+ print_status "DFU build works"
+ rm -f -r build sdkconfig
+ idf.py dfu &> tmp.log
+ grep "command \"dfu\" is not known to idf.py and is not a Ninja target" tmp.log || (tail -n 100 tmp.log ; failure "DFU build should fail for default chip target")
+ idf.py set-target esp32s2
+ idf.py dfu &> tmp.log
+ grep "build/dfu.bin\" has been written. You may proceed with DFU flashing." tmp.log || (tail -n 100 tmp.log ; failure "DFU build should succeed for esp32s2")
+ rm tmp.log
+ assert_built ${APP_BINS} ${BOOTLOADER_BINS} ${PARTITION_BIN} "dfu.bin"
+
print_status "All tests completed"
if [ -n "${FAILURES}" ]; then
echo "Some failures were detected:"
diff --git a/tools/cmake/dfu.cmake b/tools/cmake/dfu.cmake
new file mode 100644
index 0000000000..f29de4b584
--- /dev/null
+++ b/tools/cmake/dfu.cmake
@@ -0,0 +1,26 @@
+# Add DFU build and flashing related targets
+#
+
+function(__add_dfu_targets)
+ idf_build_get_property(target IDF_TARGET)
+ if(NOT "${target}" STREQUAL "esp32s2")
+ return()
+ endif()
+
+ idf_build_get_property(python PYTHON)
+ idf_build_get_property(idf_path IDF_PATH)
+
+ add_custom_target(dfu
+ COMMAND ${python} ${idf_path}/tools/mkdfu.py write
+ -o "${CMAKE_CURRENT_BINARY_DIR}/dfu.bin"
+ --json "${CMAKE_CURRENT_BINARY_DIR}/flasher_args.json"
+ DEPENDS gen_project_binary bootloader
+ VERBATIM
+ USES_TERMINAL)
+
+ add_custom_target(dfu-flash
+ COMMAND dfu-util
+ -D "${CMAKE_CURRENT_BINARY_DIR}/dfu.bin"
+ VERBATIM
+ USES_TERMINAL)
+endfunction()
diff --git a/tools/cmake/idf.cmake b/tools/cmake/idf.cmake
index 2d396c8863..82468355e9 100644
--- a/tools/cmake/idf.cmake
+++ b/tools/cmake/idf.cmake
@@ -43,6 +43,7 @@ if(NOT __idf_env_set)
include(utilities)
include(targets)
include(ldgen)
+ include(dfu)
include(version)
__build_init("${idf_path}")
diff --git a/tools/cmake/project.cmake b/tools/cmake/project.cmake
index 11b6c79166..2ec9ea4a6e 100644
--- a/tools/cmake/project.cmake
+++ b/tools/cmake/project.cmake
@@ -486,6 +486,9 @@ macro(project project_name)
unset(idf_size)
+ # Add DFU build and flash targets
+ __add_dfu_targets()
+
idf_build_executable(${project_elf})
__project_info("${test_components}")
diff --git a/tools/idf.py b/tools/idf.py
index 39716210d0..118ff5c306 100755
--- a/tools/idf.py
+++ b/tools/idf.py
@@ -447,7 +447,7 @@ def init_cli(verbose_output=None):
def _print_closing_message(self, args, actions):
# print a closing message of some kind
#
- if "flash" in str(actions):
+ if "flash" in str(actions) or "dfu" in str(actions):
print("Done")
return
diff --git a/tools/idf_py_actions/dfu_ext.py b/tools/idf_py_actions/dfu_ext.py
new file mode 100644
index 0000000000..af8d2d7fa0
--- /dev/null
+++ b/tools/idf_py_actions/dfu_ext.py
@@ -0,0 +1,39 @@
+from idf_py_actions.tools import is_target_supported, ensure_build_directory, run_target
+from idf_py_actions.errors import FatalError
+
+
+def action_extensions(base_actions, project_path):
+
+ SUPPORTED_TARGETS = ['esp32s2']
+
+ def dfu_target(target_name, ctx, args):
+ ensure_build_directory(args, ctx.info_name)
+ run_target(target_name, args)
+
+ def dfu_flash_target(target_name, ctx, args):
+ ensure_build_directory(args, ctx.info_name)
+
+ try:
+ run_target(target_name, args)
+ except FatalError:
+ # 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 '
+ 'ESP-IDF documentation for solving common dfu-util issues.')
+ raise
+
+ dfu_actions = {
+ "actions": {
+ "dfu": {
+ "callback": dfu_target,
+ "short_help": "Build the DFU binary",
+ "dependencies": ["all"],
+ },
+ "dfu-flash": {
+ "callback": dfu_flash_target,
+ "short_help": "Flash the DFU binary",
+ "order_dependencies": ["dfu"],
+ },
+ }
+ }
+
+ return dfu_actions if is_target_supported(project_path, SUPPORTED_TARGETS) else {}
diff --git a/tools/idf_py_actions/tools.py b/tools/idf_py_actions/tools.py
index 663522b82c..d96ee9f35c 100644
--- a/tools/idf_py_actions/tools.py
+++ b/tools/idf_py_actions/tools.py
@@ -269,6 +269,13 @@ def get_sdkconfig_value(sdkconfig_file, key):
return value
+def is_target_supported(project_path, supported_targets):
+ """
+ Returns True if the active target is supported, or False otherwise.
+ """
+ return get_sdkconfig_value(os.path.join(project_path, "sdkconfig"), 'CONFIG_IDF_TARGET') in supported_targets
+
+
def _guess_or_check_idf_target(args, prog_name, cache):
"""
If CMakeCache.txt doesn't exist, and IDF_TARGET is not set in the environment, guess the value from
diff --git a/tools/mkdfu.py b/tools/mkdfu.py
index eecd00b550..d2e562c717 100755
--- a/tools/mkdfu.py
+++ b/tools/mkdfu.py
@@ -1,16 +1,31 @@
#!/usr/bin/env python
#
+# Copyright 2020 Espressif Systems (Shanghai) PTE LTD
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
# This program creates archives compatible with ESP32-S* ROM DFU implementation.
#
# The archives are in CPIO format. Each file which needs to be flashed is added to the archive
# as a separate file. In addition to that, a special index file, 'dfuinfo0.dat', is created.
# This file must be the first one in the archive. It contains binary structures describing each
# subsequent file (for example, where the file needs to be flashed/loaded).
-#
-import argparse
from collections import namedtuple
+from future.utils import iteritems
+import argparse
import hashlib
+import json
import os
import struct
import zlib
@@ -21,6 +36,12 @@ except ImportError:
# Only used for type annotations
pass
+try:
+ from itertools import izip as zip
+except ImportError:
+ # Python 3
+ pass
+
# CPIO ("new ASCII") format related things
CPIO_MAGIC = b"070701"
CPIO_STRUCT = b"=6s" + b"8s" * 13
@@ -166,46 +187,64 @@ class EspDfuWriter(object):
def action_write(args):
- writer = EspDfuWriter(args.output_file)
- for addr, file in args.files:
- writer.add_file(addr, file)
+ writer = EspDfuWriter(args['output_file'])
+ for addr, f in args['files']:
+ print('Adding {} at {:#x}'.format(f, addr))
+ writer.add_file(addr, f)
writer.finish()
-
-
-class WriteArgsAction(argparse.Action):
- """ Helper for argparse to parse argument pairs """
-
- def __init__(self, *args, **kwargs):
- super(WriteArgsAction, self).__init__(*args, **kwargs)
-
- def __call__(self, parser, namespace, values, option_string=None):
- # TODO: add validation
- addr = 0
- result = []
- for i, value in enumerate(values):
- if i % 2 == 0:
- addr = int(value, 0)
- else:
- result.append((addr, value))
-
- setattr(namespace, self.dest, result)
+ print('"{}" has been written. You may proceed with DFU flashing.'.format(args['output_file'].name))
def main():
- parser = argparse.ArgumentParser("mkdfu")
+ parser = argparse.ArgumentParser()
# Provision to add "info" command
subparsers = parser.add_subparsers(dest="command")
write_parser = subparsers.add_parser("write")
- write_parser.add_argument("-o", "--output-file", type=argparse.FileType("wb"))
- write_parser.add_argument(
- "files", metavar=" ", action=WriteArgsAction, nargs="+"
- )
+ write_parser.add_argument("-o", "--output-file",
+ help='Filename for storing the output DFU image',
+ required=True,
+ type=argparse.FileType("wb"))
+ write_parser.add_argument("--json",
+ help='Optional file for loading "flash_files" dictionary with items')
+ write_parser.add_argument("files",
+ metavar=" ", help='Add at ',
+ nargs="*")
args = parser.parse_args()
- print(repr(args))
- if args.command == "write":
- action_write(args)
+
+ def check_file(file_name):
+ if not os.path.isfile(file_name):
+ raise RuntimeError('{} is not a regular file!'.format(file_name))
+ return file_name
+
+ files = []
+ if args.files:
+ files += [(int(addr, 0), check_file(f_name)) for addr, f_name in zip(args.files[::2], args.files[1::2])]
+
+ if args.json:
+ json_dir = os.path.dirname(os.path.abspath(args.json))
+
+ def process_json_file(path):
+ '''
+ The input path is relative to json_dir. This function makes it relative to the current working
+ directory.
+ '''
+ return check_file(os.path.relpath(os.path.join(json_dir, path), start=os.curdir))
+
+ with open(args.json) as f:
+ files += [(int(addr, 0),
+ process_json_file(f_name)) for addr, f_name in iteritems(json.load(f)['flash_files'])]
+
+ files = sorted([(addr, f_name) for addr, f_name in iteritems(dict(files))],
+ key=lambda x: x[0]) # remove possible duplicates and sort based on the address
+
+ cmd_args = {'output_file': args.output_file,
+ 'files': files,
+ }
+
+ {'write': action_write
+ }[args.command](cmd_args)
if __name__ == "__main__":
diff --git a/tools/test_mkdfu/1/1.bin b/tools/test_mkdfu/1/1.bin
new file mode 100644
index 0000000000..60320aa0df
Binary files /dev/null and b/tools/test_mkdfu/1/1.bin differ
diff --git a/tools/test_mkdfu/1/2.bin b/tools/test_mkdfu/1/2.bin
new file mode 100644
index 0000000000..1b9950f09d
Binary files /dev/null and b/tools/test_mkdfu/1/2.bin differ
diff --git a/tools/test_mkdfu/1/3.bin b/tools/test_mkdfu/1/3.bin
new file mode 100644
index 0000000000..08369e8490
Binary files /dev/null and b/tools/test_mkdfu/1/3.bin differ
diff --git a/tools/test_mkdfu/1/dfu.bin b/tools/test_mkdfu/1/dfu.bin
new file mode 100644
index 0000000000..74f9fef52c
Binary files /dev/null and b/tools/test_mkdfu/1/dfu.bin differ
diff --git a/tools/test_mkdfu/1/flasher_args.json b/tools/test_mkdfu/1/flasher_args.json
new file mode 100644
index 0000000000..06a54d0cc7
--- /dev/null
+++ b/tools/test_mkdfu/1/flasher_args.json
@@ -0,0 +1,7 @@
+{
+ "flash_files" : {
+ "0x8000" : "2.bin",
+ "0x1000" : "1.bin",
+ "0x10000" : "3.bin"
+ }
+}
diff --git a/tools/test_mkdfu/test_mkdfu.py b/tools/test_mkdfu/test_mkdfu.py
new file mode 100755
index 0000000000..1ce60c227d
--- /dev/null
+++ b/tools/test_mkdfu/test_mkdfu.py
@@ -0,0 +1,99 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# Copyright 2020 Espressif Systems (Shanghai) CO LTD
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from __future__ import unicode_literals
+import filecmp
+import os
+import pexpect
+import shutil
+import sys
+import tempfile
+import time
+import unittest
+
+current_dir = os.path.dirname(os.path.realpath(__file__))
+mkdfu_path = os.path.join(current_dir, '..', 'mkdfu.py')
+
+
+class TestHelloWorldExample(unittest.TestCase):
+ def common_test(self, add_args):
+ with tempfile.NamedTemporaryFile(delete=False) as f:
+ self.addCleanup(os.unlink, f.name)
+ cmd = ' '.join([sys.executable, mkdfu_path, 'write',
+ '-o', f.name,
+ add_args])
+ p = pexpect.spawn(cmd, timeout=10)
+ self.addCleanup(p.terminate, force=True)
+
+ p.expect_exact(['Adding 1/bootloader.bin at 0x1000',
+ 'Adding 1/partition-table.bin at 0x8000',
+ 'Adding 1/hello-world.bin at 0x10000',
+ '"{}" has been written. You may proceed with DFU flashing.'.format(f.name)])
+
+ # Need to wait for the process to end because the output file is closed when mkdfu exits.
+ # Do non-blocking wait instead of the blocking p.wait():
+ for _ in range(10):
+ if not p.isalive():
+ break
+ time.sleep(0.5)
+ else:
+ p.terminate()
+
+ self.assertTrue(filecmp.cmp(f.name, os.path.join(current_dir, '1','dfu.bin')), 'Output files are different')
+
+ def test_with_json(self):
+ self.common_test(' '.join(['--json', os.path.join(current_dir, '1', 'flasher_args.json')]))
+
+ def test_without_json(self):
+
+ self.common_test(' '.join(['0x1000', os.path.join(current_dir, '1', '1.bin'),
+ '0x8000', os.path.join(current_dir, '1', '2.bin'),
+ '0x10000', os.path.join(current_dir, '1', '3.bin')
+ ]))
+
+ def test_filenames(self):
+ temp_dir = tempfile.mkdtemp(prefix='very_long_directory_name' * 8)
+ self.addCleanup(shutil.rmtree, temp_dir, ignore_errors=True)
+
+ with tempfile.NamedTemporaryFile(dir=temp_dir, delete=False) as f:
+ output = f.name
+
+ with tempfile.NamedTemporaryFile(prefix='ľščťžýáíéěř\u0420\u043e\u0441\u0441\u0438\u044f',
+ dir=temp_dir,
+ delete=False) as f:
+ bootloader = f.name
+
+ shutil.copyfile(os.path.join(current_dir, '1', '1.bin'), bootloader)
+
+ cmd = ' '.join([sys.executable, mkdfu_path, 'write',
+ '-o', output,
+ ' '.join(['0x1000', bootloader,
+ '0x8000', os.path.join(current_dir, '1', '2.bin'),
+ '0x10000', os.path.join(current_dir, '1', '3.bin')
+ ])
+ ])
+ p = pexpect.spawn(cmd, timeout=10, encoding='utf-8')
+ self.addCleanup(p.terminate, force=True)
+
+ p.expect_exact(['Adding {} at 0x1000'.format(bootloader),
+ 'Adding 1/2.bin at 0x8000',
+ 'Adding 1/3.bin at 0x10000',
+ '"{}" has been written. You may proceed with DFU flashing.'.format(output)])
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/tools/tools.json b/tools/tools.json
index f1c93c2f66..840a734e68 100644
--- a/tools/tools.json
+++ b/tools/tools.json
@@ -483,6 +483,43 @@
}
}
]
+ },
+ {
+ "description": "dfu-util (Device Firmware Upgrade Utilities)",
+ "export_paths": [
+ [
+ "dfu-util-0.9-win64"
+ ]
+ ],
+ "export_vars": {},
+ "info_url": "http://dfu-util.sourceforge.net/",
+ "install": "never",
+ "license": "GPL-2.0-only",
+ "name": "dfu-util",
+ "platform_overrides": [
+ {
+ "install": "always",
+ "platforms": [
+ "win64"
+ ]
+ }
+ ],
+ "version_cmd": [
+ "dfu-util",
+ "--version"
+ ],
+ "version_regex": "dfu-util ([0-9.]+)",
+ "versions": [
+ {
+ "name": "0.9",
+ "status": "recommended",
+ "win64": {
+ "sha256": "5816d7ec68ef3ac07b5ac9fb9837c57d2efe45b6a80a2f2bbe6b40b1c15c470e",
+ "size": 735635,
+ "url": "https://dl.espressif.com/dl/dfu-util-0.9-win64.zip"
+ }
+ }
+ ]
}
],
"version": 1