mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
Merge branch 'feature/dfu_build_flash_s2' into 'master'
Add build system support for programming ESP32-S2 using DFU utils Closes IDF-579 See merge request espressif/esp-idf!8294
This commit is contained in:
commit
f9552baf97
@ -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']:
|
||||
|
99
docs/en/api-guides/dfu.rst
Normal file
99
docs/en/api-guides/dfu.rst
Normal file
@ -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 <http://dfu-util.sourceforge.net/>`_. 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 <https://github.com/libusb/libusb/wiki/Windows#How_to_use_libusb_on_Windows>`_ 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`).
|
@ -11,6 +11,7 @@ API Guides
|
||||
Build System <build-system>
|
||||
:esp32: Build System (Legacy GNU Make) <build-system-legacy>
|
||||
Deep Sleep Wake Stubs <deep-sleep-stub>
|
||||
:esp32s2: Device Firmware Upgrade through USB <dfu>
|
||||
Error Handling <error-handling>
|
||||
:esp32: ESP-BLE-MESH <esp-ble-mesh/ble-mesh-index>
|
||||
ESP-MESH (Wi-Fi) <mesh>
|
||||
|
@ -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
|
||||
|
||||
|
||||
---
|
||||
|
@ -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".
|
||||
|
@ -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".
|
||||
|
@ -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
|
||||
=================================
|
||||
|
@ -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.
|
||||
|
||||
|
1
docs/zh_CN/api-guides/dfu.rst
Normal file
1
docs/zh_CN/api-guides/dfu.rst
Normal file
@ -0,0 +1 @@
|
||||
.. include:: ../../en/api-guides/dfu.rst
|
@ -12,6 +12,7 @@ API 指南
|
||||
严重错误 <fatal-errors>
|
||||
Event Handling <event-handling>
|
||||
Deep Sleep Wake Stubs <deep-sleep-stub>
|
||||
:esp32s2: Device Firmware Upgrade through USB <dfu>
|
||||
ESP32 Core Dump <core_dump>
|
||||
Flash Encryption <../security/flash-encryption>
|
||||
FreeRTOS SMP Changes <freertos-smp>
|
||||
|
@ -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
|
||||
|
||||
|
||||
---
|
||||
|
@ -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"。
|
||||
|
@ -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"。
|
||||
|
@ -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
|
||||
|
||||
从源代码编译工具链
|
||||
=================================
|
||||
|
@ -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 平台的下载安装问题。
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -707,6 +707,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:"
|
||||
|
26
tools/cmake/dfu.cmake
Normal file
26
tools/cmake/dfu.cmake
Normal file
@ -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()
|
@ -43,6 +43,7 @@ if(NOT __idf_env_set)
|
||||
include(utilities)
|
||||
include(targets)
|
||||
include(ldgen)
|
||||
include(dfu)
|
||||
include(version)
|
||||
|
||||
__build_init("${idf_path}")
|
||||
|
@ -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}")
|
||||
|
@ -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
|
||||
|
||||
|
39
tools/idf_py_actions/dfu_ext.py
Normal file
39
tools/idf_py_actions/dfu_ext.py
Normal file
@ -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 {}
|
@ -270,6 +270,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
|
||||
|
251
tools/mkdfu.py
Executable file
251
tools/mkdfu.py
Executable file
@ -0,0 +1,251 @@
|
||||
#!/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).
|
||||
|
||||
from collections import namedtuple
|
||||
from future.utils import iteritems
|
||||
import argparse
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
import struct
|
||||
import zlib
|
||||
|
||||
try:
|
||||
import typing
|
||||
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
|
||||
CPIOHeader = namedtuple(
|
||||
"CPIOHeader",
|
||||
[
|
||||
"magic",
|
||||
"ino",
|
||||
"mode",
|
||||
"uid",
|
||||
"gid",
|
||||
"nlink",
|
||||
"mtime",
|
||||
"filesize",
|
||||
"devmajor",
|
||||
"devminor",
|
||||
"rdevmajor",
|
||||
"rdevminor",
|
||||
"namesize",
|
||||
"check",
|
||||
],
|
||||
)
|
||||
CPIO_TRAILER = "TRAILER!!!"
|
||||
|
||||
|
||||
def make_cpio_header(
|
||||
filename_len, file_len, is_trailer=False
|
||||
): # type: (int, int, bool) -> CPIOHeader
|
||||
""" Returns CPIOHeader for the given file name and file size """
|
||||
|
||||
def as_hex(val): # type: (int) -> bytes
|
||||
return "{:08x}".format(val).encode("ascii")
|
||||
|
||||
hex_0 = as_hex(0)
|
||||
mode = hex_0 if is_trailer else as_hex(0o0100644)
|
||||
nlink = as_hex(1) if is_trailer else hex_0
|
||||
return CPIOHeader(
|
||||
magic=CPIO_MAGIC,
|
||||
ino=hex_0,
|
||||
mode=mode,
|
||||
uid=hex_0,
|
||||
gid=hex_0,
|
||||
nlink=nlink,
|
||||
mtime=hex_0,
|
||||
filesize=as_hex(file_len),
|
||||
devmajor=hex_0,
|
||||
devminor=hex_0,
|
||||
rdevmajor=hex_0,
|
||||
rdevminor=hex_0,
|
||||
namesize=as_hex(filename_len),
|
||||
check=hex_0,
|
||||
)
|
||||
|
||||
|
||||
# DFU format related things
|
||||
# Structure of one entry in dfuinfo0.dat
|
||||
DFUINFO_STRUCT = b"<I I 64s 16s"
|
||||
DFUInfo = namedtuple("DFUInfo", ["address", "flags", "name", "md5"])
|
||||
DFUINFO_FILE = "dfuinfo0.dat"
|
||||
# Structure which gets added at the end of the entire DFU file
|
||||
DFUSUFFIX_STRUCT = b"<H H H H 3s B"
|
||||
DFUSuffix = namedtuple(
|
||||
"DFUSuffix", ["bcd_device", "pid", "vid", "bcd_dfu", "sig", "len"]
|
||||
)
|
||||
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
|
||||
DFUCRC_STRUCT = b"<I"
|
||||
|
||||
|
||||
def dfu_crc(data, crc=0): # type: (bytes, int) -> int
|
||||
""" Calculate CRC32/JAMCRC of data, with an optional initial value """
|
||||
uint32_max = 0xFFFFFFFF
|
||||
return uint32_max - (zlib.crc32(data, crc) & uint32_max)
|
||||
|
||||
|
||||
def pad_bytes(b, multiple, padding=b"\x00"): # type: (bytes, int, bytes) -> bytes
|
||||
""" Pad 'b' to a length divisible by 'multiple' """
|
||||
padded_len = (len(b) + multiple - 1) // multiple * multiple
|
||||
return b + padding * (padded_len - len(b))
|
||||
|
||||
|
||||
class EspDfuWriter(object):
|
||||
def __init__(self, dest_file): # type: (typing.BinaryIO) -> None
|
||||
self.dest = dest_file
|
||||
self.entries = [] # type: typing.List[bytes]
|
||||
self.index = [] # type: typing.List[DFUInfo]
|
||||
|
||||
def add_file(self, flash_addr, path): # type: (int, str) -> None
|
||||
""" Add file to be written into flash at given address """
|
||||
with open(path, "rb") as f:
|
||||
self._add_cpio_flash_entry(os.path.basename(path), flash_addr, f.read())
|
||||
|
||||
def finish(self): # type: () -> None
|
||||
""" Write DFU file """
|
||||
# Prepare and add dfuinfo0.dat file
|
||||
dfuinfo = b"".join([struct.pack(DFUINFO_STRUCT, *item) for item in self.index])
|
||||
self._add_cpio_entry(DFUINFO_FILE, dfuinfo, first=True)
|
||||
|
||||
# Add CPIO archive trailer
|
||||
self._add_cpio_entry(CPIO_TRAILER, b"", trailer=True)
|
||||
|
||||
# Combine all the entries and pad the file
|
||||
out_data = b"".join(self.entries)
|
||||
cpio_block_size = 10240
|
||||
out_data = pad_bytes(out_data, cpio_block_size)
|
||||
|
||||
# Add DFU suffix and CRC
|
||||
out_data += struct.pack(DFUSUFFIX_STRUCT, *DFUSUFFIX_DEFAULT)
|
||||
out_data += struct.pack(DFUCRC_STRUCT, dfu_crc(out_data))
|
||||
|
||||
# Finally write the entire binary
|
||||
self.dest.write(out_data)
|
||||
|
||||
def _add_cpio_flash_entry(
|
||||
self, filename, flash_addr, data
|
||||
): # type: (str, int, bytes) -> None
|
||||
md5 = hashlib.md5()
|
||||
md5.update(data)
|
||||
self.index.append(
|
||||
DFUInfo(
|
||||
address=flash_addr,
|
||||
flags=0,
|
||||
name=filename.encode("utf-8"),
|
||||
md5=md5.digest(),
|
||||
)
|
||||
)
|
||||
self._add_cpio_entry(filename, data)
|
||||
|
||||
def _add_cpio_entry(
|
||||
self, filename, data, first=False, trailer=False
|
||||
): # type: (str, bytes, bool, bool) -> None
|
||||
filename_b = filename.encode("utf-8") + b"\x00"
|
||||
cpio_header = make_cpio_header(len(filename_b), len(data), is_trailer=trailer)
|
||||
entry = pad_bytes(
|
||||
struct.pack(CPIO_STRUCT, *cpio_header) + filename_b, 4
|
||||
) + pad_bytes(data, 4)
|
||||
if not first:
|
||||
self.entries.append(entry)
|
||||
else:
|
||||
self.entries.insert(0, entry)
|
||||
|
||||
|
||||
def action_write(args):
|
||||
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()
|
||||
print('"{}" has been written. You may proceed with DFU flashing.'.format(args['output_file'].name))
|
||||
|
||||
|
||||
def main():
|
||||
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",
|
||||
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 <address> <file> items')
|
||||
write_parser.add_argument("files",
|
||||
metavar="<address> <file>", help='Add <file> at <address>',
|
||||
nargs="*")
|
||||
|
||||
args = parser.parse_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__":
|
||||
main()
|
BIN
tools/test_mkdfu/1/1.bin
Normal file
BIN
tools/test_mkdfu/1/1.bin
Normal file
Binary file not shown.
BIN
tools/test_mkdfu/1/2.bin
Normal file
BIN
tools/test_mkdfu/1/2.bin
Normal file
Binary file not shown.
BIN
tools/test_mkdfu/1/3.bin
Normal file
BIN
tools/test_mkdfu/1/3.bin
Normal file
Binary file not shown.
BIN
tools/test_mkdfu/1/dfu.bin
Normal file
BIN
tools/test_mkdfu/1/dfu.bin
Normal file
Binary file not shown.
7
tools/test_mkdfu/1/flasher_args.json
Normal file
7
tools/test_mkdfu/1/flasher_args.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"flash_files" : {
|
||||
"0x8000" : "2.bin",
|
||||
"0x1000" : "1.bin",
|
||||
"0x10000" : "3.bin"
|
||||
}
|
||||
}
|
99
tools/test_mkdfu/test_mkdfu.py
Executable file
99
tools/test_mkdfu/test_mkdfu.py
Executable file
@ -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()
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user