diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index fdebe9f0b63..547977f3eb7 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -30,15 +30,11 @@ name: Build on: [push, pull_request] -jobs: +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || (github.repository == 'openthread/openthread' && github.run_id) || github.ref }} + cancel-in-progress: true - cancel-previous-runs: - runs-on: ubuntu-20.04 - steps: - - uses: rokroskar/workflow-run-cleanup-action@master - env: - GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" - if: "github.ref != 'refs/heads/main'" +jobs: pretty: runs-on: ubuntu-20.04 @@ -180,6 +176,8 @@ jobs: sudo pip3 install -U cmake==3.10.3 cmake --version | grep 3.10.3 - name: Build + env: + GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" run: | export PATH=/tmp/${{ matrix.gcc_extract_dir }}/bin:$PATH script/check-arm-build diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 339bb1a09a7..534dcf7c41b 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -30,15 +30,11 @@ name: Docker on: [push, pull_request] -jobs: +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || (github.repository == 'openthread/openthread' && github.run_id) || github.ref }} + cancel-in-progress: true - cancel-previous-runs: - runs-on: ubuntu-20.04 - steps: - - uses: rokroskar/workflow-run-cleanup-action@master - env: - GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" - if: "github.ref != 'refs/heads/main'" +jobs: buildx: name: buildx-${{ matrix.docker_name }} diff --git a/.github/workflows/fuzz.yml b/.github/workflows/fuzz.yml index 8f549c50514..bce537431d2 100644 --- a/.github/workflows/fuzz.yml +++ b/.github/workflows/fuzz.yml @@ -27,7 +27,13 @@ # name: CIFuzz + on: [pull_request] + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || (github.repository == 'openthread/openthread' && github.run_id) || github.ref }} + cancel-in-progress: true + jobs: Fuzzing: runs-on: ubuntu-20.04 diff --git a/.github/workflows/makefile-check.yml b/.github/workflows/makefile-check.yml index 37e80243cf0..4f955672710 100644 --- a/.github/workflows/makefile-check.yml +++ b/.github/workflows/makefile-check.yml @@ -30,15 +30,11 @@ name: Makefile Check on: [push, pull_request] -jobs: - cancel-previous-runs: - runs-on: ubuntu-20.04 - steps: - - uses: rokroskar/workflow-run-cleanup-action@master - env: - GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" - if: "github.ref != 'refs/heads/main'" +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || (github.repository == 'openthread/openthread' && github.run_id) || github.ref }} + cancel-in-progress: true +jobs: makefile-check: runs-on: ubuntu-20.04 steps: diff --git a/.github/workflows/otbr.yml b/.github/workflows/otbr.yml index 6f9412b8665..4e7d899cca0 100644 --- a/.github/workflows/otbr.yml +++ b/.github/workflows/otbr.yml @@ -30,15 +30,11 @@ name: Border Router on: [push, pull_request] -jobs: +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || (github.repository == 'openthread/openthread' && github.run_id) || github.ref }} + cancel-in-progress: true - cancel-previous-runs: - runs-on: ubuntu-20.04 - steps: - - uses: rokroskar/workflow-run-cleanup-action@master - env: - GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" - if: "github.ref != 'refs/heads/main'" +jobs: backbone-router: runs-on: ubuntu-20.04 diff --git a/.github/workflows/otci.yml b/.github/workflows/otci.yml index a1e3a9b99df..1a88ee686c1 100644 --- a/.github/workflows/otci.yml +++ b/.github/workflows/otci.yml @@ -30,15 +30,11 @@ name: OTCI on: [push, pull_request] -jobs: +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || (github.repository == 'openthread/openthread' && github.run_id) || github.ref }} + cancel-in-progress: true - cancel-previous-runs: - runs-on: ubuntu-20.04 - steps: - - uses: rokroskar/workflow-run-cleanup-action@master - env: - GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" - if: "github.ref != 'refs/heads/main'" +jobs: cli-sim: name: cli-sim VIRTUAL_TIME=${{ matrix.virtual_time }} diff --git a/.github/workflows/otns.yml b/.github/workflows/otns.yml index 46dc33c65e6..a916a6e0176 100644 --- a/.github/workflows/otns.yml +++ b/.github/workflows/otns.yml @@ -30,6 +30,10 @@ name: OTNS on: [push, pull_request] +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || (github.repository == 'openthread/openthread' && github.run_id) || github.ref }} + cancel-in-progress: true + env: COVERAGE: 1 REFERENCE_DEVICE: 1 @@ -40,14 +44,6 @@ env: jobs: - cancel-previous-runs: - runs-on: ubuntu-20.04 - steps: - - uses: rokroskar/workflow-run-cleanup-action@master - env: - GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" - if: "github.ref != 'refs/heads/main'" - unittests: name: Unittests runs-on: ubuntu-20.04 diff --git a/.github/workflows/posix.yml b/.github/workflows/posix.yml index 3776b11e1b9..552db037ce6 100644 --- a/.github/workflows/posix.yml +++ b/.github/workflows/posix.yml @@ -30,15 +30,11 @@ name: POSIX on: [push, pull_request] -jobs: +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || (github.repository == 'openthread/openthread' && github.run_id) || github.ref }} + cancel-in-progress: true - cancel-previous-runs: - runs-on: ubuntu-20.04 - steps: - - uses: rokroskar/workflow-run-cleanup-action@master - env: - GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" - if: "github.ref != 'refs/heads/main'" +jobs: expects-linux: runs-on: ubuntu-20.04 @@ -110,6 +106,17 @@ jobs: with: name: cov-expects-linux-2 path: tmp/coverage.info + + tcplp-buffering: + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v2 + with: + submodules: true + - name: Build + run: make -C third_party/tcplp/lib/test/ + - name: Run + run: third_party/tcplp/lib/test/test_all thread-cert: runs-on: ubuntu-20.04 diff --git a/.github/workflows/simulation-1.1.yml b/.github/workflows/simulation-1.1.yml index 3fadd17aa81..e3c269c07cf 100644 --- a/.github/workflows/simulation-1.1.yml +++ b/.github/workflows/simulation-1.1.yml @@ -30,15 +30,11 @@ name: Simulation 1.1 on: [push, pull_request] -jobs: +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || (github.repository == 'openthread/openthread' && github.run_id) || github.ref }} + cancel-in-progress: true - cancel-previous-runs: - runs-on: ubuntu-20.04 - steps: - - uses: rokroskar/workflow-run-cleanup-action@master - env: - GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" - if: "github.ref != 'refs/heads/main'" +jobs: distcheck: runs-on: ubuntu-20.04 diff --git a/.github/workflows/simulation-1.2.yml b/.github/workflows/simulation-1.2.yml index 26166466d98..d099110ab51 100644 --- a/.github/workflows/simulation-1.2.yml +++ b/.github/workflows/simulation-1.2.yml @@ -30,15 +30,11 @@ name: Simulation 1.3 on: [push, pull_request] -jobs: +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || (github.repository == 'openthread/openthread' && github.run_id) || github.ref }} + cancel-in-progress: true - cancel-previous-runs: - runs-on: ubuntu-20.04 - steps: - - uses: rokroskar/workflow-run-cleanup-action@master - env: - GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" - if: "github.ref != 'refs/heads/main'" +jobs: thread-1-3: name: thread-1-3-${{ matrix.compiler.c }}-${{ matrix.arch }} diff --git a/.github/workflows/size.yml b/.github/workflows/size.yml index 763fb4584ad..2fae6778e53 100644 --- a/.github/workflows/size.yml +++ b/.github/workflows/size.yml @@ -30,15 +30,11 @@ name: Size on: [push, pull_request] -jobs: +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || (github.repository == 'openthread/openthread' && github.run_id) || github.ref }} + cancel-in-progress: true - cancel-previous-runs: - runs-on: ubuntu-20.04 - steps: - - uses: rokroskar/workflow-run-cleanup-action@master - env: - GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" - if: "github.ref != 'refs/heads/main'" +jobs: size-report: runs-on: ubuntu-20.04 diff --git a/.github/workflows/toranj.yml b/.github/workflows/toranj.yml index 8af954d8bdc..2d1f23b9e47 100644 --- a/.github/workflows/toranj.yml +++ b/.github/workflows/toranj.yml @@ -30,15 +30,11 @@ name: Toranj on: [push, pull_request] -jobs: +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || (github.repository == 'openthread/openthread' && github.run_id) || github.ref }} + cancel-in-progress: true - cancel-previous-runs: - runs-on: ubuntu-20.04 - steps: - - uses: rokroskar/workflow-run-cleanup-action@master - env: - GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" - if: "github.ref != 'refs/heads/main'" +jobs: toranj-ncp: name: toranj-ncp-${{ matrix.TORANJ_RADIO }} @@ -123,6 +119,27 @@ jobs: name: cov-toranj-cli-${{ matrix.TORANJ_RADIO }} path: tmp/coverage.info + toranj-unittest: + name: toranj-unittest + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v2 + with: + submodules: true + - name: Bootstrap + env: + GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" + run: | + sudo dpkg --add-architecture i386 + sudo apt-get update + sudo apt-get --no-install-recommends install -y clang-10 clang++-10 ninja-build python3-setuptools python3-wheel llvm lcov + sudo apt-get --no-install-recommends install -y g++-multilib libreadline-dev:i386 libncurses-dev:i386 + python3 -m pip install -r tests/scripts/thread-cert/requirements.txt + - name: Build & Run + run: | + ./tests/toranj/build.sh cmake + ninja test + upload-coverage: needs: - toranj-ncp diff --git a/.github/workflows/version.yml b/.github/workflows/version.yml index 37b110ca8ed..6246f208149 100644 --- a/.github/workflows/version.yml +++ b/.github/workflows/version.yml @@ -30,15 +30,11 @@ name: API Version on: [pull_request] -jobs: - cancel-previous-runs: - runs-on: ubuntu-20.04 - steps: - - uses: rokroskar/workflow-run-cleanup-action@master - env: - GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" - if: "github.ref != 'refs/heads/main'" +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || (github.repository == 'openthread/openthread' && github.run_id) || github.ref }} + cancel-in-progress: true +jobs: api-version: runs-on: ubuntu-20.04 steps: diff --git a/Android.mk b/Android.mk index cd7e7e45abf..2cd8a5bc668 100644 --- a/Android.mk +++ b/Android.mk @@ -213,6 +213,7 @@ LOCAL_SRC_FILES := \ src/core/api/srp_server_api.cpp \ src/core/api/tasklet_api.cpp \ src/core/api/tcp_api.cpp \ + src/core/api/tcp_ext_api.cpp \ src/core/api/thread_api.cpp \ src/core/api/thread_ftd_api.cpp \ src/core/api/trel_api.cpp \ @@ -317,6 +318,7 @@ LOCAL_SRC_FILES := \ src/core/net/srp_client.cpp \ src/core/net/srp_server.cpp \ src/core/net/tcp6.cpp \ + src/core/net/tcp6_ext.cpp \ src/core/net/udp6.cpp \ src/core/radio/radio.cpp \ src/core/radio/radio_callbacks.cpp \ diff --git a/configure.ac b/configure.ac index 0394a7d8214..c86126d2f8f 100644 --- a/configure.ac +++ b/configure.ac @@ -828,14 +828,14 @@ AC_MSG_CHECKING([whether to build examples]) AC_ARG_WITH(examples, [AS_HELP_STRING([--with-examples=TARGET], - [Build example applications for one of: simulation, cc2538 @<:@default=no@:>@. + [Build example applications for one of: simulation @<:@default=no@:>@. Note that building example applications also builds the associated OpenThread platform libraries and any third_party libraries needed to support the examples.])], [ case "${with_examples}" in no) ;; - simulation|cc2538) + simulation) ;; *) AC_MSG_RESULT(ERROR) @@ -848,7 +848,6 @@ AC_ARG_WITH(examples, AM_CONDITIONAL([OPENTHREAD_ENABLE_EXAMPLES], [test ${with_examples} != "no"]) AM_CONDITIONAL([OPENTHREAD_EXAMPLES_SIMULATION],[test "${with_examples}" = "simulation"]) -AM_CONDITIONAL([OPENTHREAD_EXAMPLES_CC2538], [test "${with_examples}" = "cc2538"]) AM_COND_IF([OPENTHREAD_EXAMPLES_SIMULATION], CPPFLAGS="${CPPFLAGS} -DOPENTHREAD_EXAMPLES_SIMULATION=1", CPPFLAGS="${CPPFLAGS} -DOPENTHREAD_EXAMPLES_SIMULATION=0") @@ -869,11 +868,11 @@ AC_MSG_CHECKING([whether to build platform libraries]) AC_ARG_WITH(platform, [AS_HELP_STRING([--with-platform=TARGET], - [Build OpenThread platform libraries for one of: cc2538, posix, simulation @<:@default=simulation@:>@.])], + [Build OpenThread platform libraries for one of: posix, simulation @<:@default=simulation@:>@.])], [ # Make sure the given target is valid. case "${with_platform}" in - no|cc2538|posix|simulation) + no|posix|simulation) ;; *) AC_MSG_RESULT(ERROR) @@ -904,7 +903,6 @@ AM_CONDITIONAL([OPENTHREAD_ENABLE_PLATFORM], [test ${with_platform} != "no"]) OPENTHREAD_ENABLE_PLATFORM=${with_platform} -AM_CONDITIONAL([OPENTHREAD_PLATFORM_CC2538], [test "${with_platform}" = "cc2538"]) AM_CONDITIONAL([OPENTHREAD_PLATFORM_POSIX], [test "${with_platform}" = "posix"]) AM_CONDITIONAL([OPENTHREAD_PLATFORM_SIMULATION],[test "${with_platform}" = "simulation"]) @@ -1047,7 +1045,6 @@ examples/apps/Makefile examples/apps/cli/Makefile examples/apps/ncp/Makefile examples/platforms/Makefile -examples/platforms/cc2538/Makefile examples/platforms/simulation/Makefile examples/platforms/utils/Makefile tools/Makefile diff --git a/examples/Makefile-cc2538 b/examples/Makefile-cc2538 deleted file mode 100644 index 99d895a53a6..00000000000 --- a/examples/Makefile-cc2538 +++ /dev/null @@ -1,309 +0,0 @@ -# -# Copyright (c) 2016, The OpenThread Authors. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# 3. Neither the name of the copyright holder nor the -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# - -.NOTPARALLEL: - -AR = arm-none-eabi-ar -CCAS = arm-none-eabi-as -CPP = arm-none-eabi-cpp -CC = arm-none-eabi-gcc -CXX = arm-none-eabi-g++ -LD = arm-none-eabi-ld -STRIP = arm-none-eabi-strip -NM = arm-none-eabi-nm -RANLIB = arm-none-eabi-ranlib -OBJCOPY = arm-none-eabi-objcopy - -BuildJobs ?= 10 - -configure_OPTIONS = \ - --enable-cli \ - --enable-ftd \ - --enable-mtd \ - --enable-ncp \ - --enable-radio-only \ - --enable-linker-map \ - --with-examples=cc2538 \ - $(NULL) - -TopSourceDir := $(dir $(shell readlink $(firstword $(MAKEFILE_LIST)))).. -AbsTopSourceDir := $(dir $(realpath $(firstword $(MAKEFILE_LIST)))).. - -CC2538_CONFIG_FILE_CPPFLAGS = -DOPENTHREAD_PROJECT_CORE_CONFIG_FILE='\"openthread-core-cc2538-config.h\"' -CC2538_CONFIG_FILE_CPPFLAGS += -DOPENTHREAD_CORE_CONFIG_PLATFORM_CHECK_FILE='\"openthread-core-cc2538-config-check.h\"' -CC2538_CONFIG_FILE_CPPFLAGS += -I$(AbsTopSourceDir)/examples/platforms/cc2538/ - -COMMONCFLAGS := \ - -fdata-sections \ - -ffunction-sections \ - -Os \ - -g \ - $(CC2538_CONFIG_FILE_CPPFLAGS) \ - $(NULL) - -include $(dir $(abspath $(lastword $(MAKEFILE_LIST))))/common-switches.mk - -# Optional CC2592 options, first and foremost, whether to enable support for it -# at all. -ifeq ($(CC2592),1) -COMMONCFLAGS += -DOPENTHREAD_CONFIG_CC2538_WITH_CC2592=1 - -# If the PA_EN is on another port C pin, specify it with CC2592_PA_PIN. -ifneq ($(CC2592_PA_EN),) -COMMONCFLAGS += -DOPENTHREAD_CONFIG_CC2592_PA_EN_PIN=$(CC2592_PA_EN) -endif - -# If the LNA_EN is on another port C pin, specify it with CC2592_LNA_PIN. -ifneq ($(CC2592_LNA_EN),) -COMMONCFLAGS += -DOPENTHREAD_CONFIG_CC2592_LNA_EN_PIN=$(CC2592_LNA_EN) -endif - -# If we're not using HGM, set CC2538_USE_HGM to 0. -ifeq ($(CC2592_USE_HGM),0) -COMMONCFLAGS += -DOPENTHREAD_CONFIG_CC2592_USE_HGM=0 -else # CC2592_USE_HGM=1 - -# HGM in use, if not on port D, specify the port here (A, B or C) with CC2592_HGM_PORT. -ifneq ($(CC2592_HGM_PORT),) -COMMONCFLAGS += -DOPENTHREAD_CONFIG_CC2592_HGM_PORT=GPIO_$(CC2592_HGM_PORT)_BASE -endif - -# If HGM is not at pin 2, specify which pin here with CC2592_HGM_PIN. -ifneq ($(CC2592_HGM_PIN),) -COMMONCFLAGS += -DOPENTHREAD_CONFIG_CC2592_HGM_PIN=$(CC2592_HGM_PIN) -endif - -# If we want it off by default, specify CC2592_HGM_DEFAULT_STATE=0 -ifeq ($(CC2592_HGM_DEFAULT_STATE),0) -COMMONCFLAGS += -DOPENTHREAD_CONFIG_CC2592_HGM_DEFAULT_STATE=false -endif - -endif # CC2592_USE_HGM - -endif # CC2592 - -ifneq ($(CC2538_RECEIVE_SENSITIVITY),) -COMMONCFLAGS += -DOPENTHREAD_CONFIG_CC2538_RECEIVE_SENSITIVITY=$(CC2538_RECEIVE_SENSITIVITY) -endif - -ifneq ($(CC2538_RSSI_OFFSET),) -COMMONCFLAGS += -DOPENTHREAD_CONFIG_CC2538_RSSI_OFFSET=$(CC2538_RSSI_OFFSET) -endif - -CPPFLAGS += \ - $(COMMONCFLAGS) \ - $(target_CPPFLAGS) \ - $(NULL) - -CFLAGS += \ - $(COMMONCFLAGS) \ - $(target_CFLAGS) \ - $(NULL) - -CXXFLAGS += \ - $(COMMONCFLAGS) \ - $(target_CXXFLAGS) \ - -fno-exceptions \ - -fno-rtti \ - $(NULL) - -LDFLAGS += \ - $(COMMONCFLAGS) \ - $(target_LDFLAGS) \ - -nostartfiles \ - -specs=nano.specs \ - -specs=nosys.specs \ - -Wl,--gc-sections \ - $(NULL) - -ECHO := @echo -MAKE := make -MKDIR_P := mkdir -p -LN_S := ln -s -RM_F := rm -f - -INSTALL := /usr/bin/install -INSTALLFLAGS := -p - -BuildPath = build -TopBuildDir = $(BuildPath) -AbsTopBuildDir = $(PWD)/$(TopBuildDir) - -ResultPath = output -TopResultDir = $(ResultPath) -AbsTopResultDir = $(PWD)/$(TopResultDir) - -TargetTuple = cc2538 - -ARCHS = cortex-m3 - -TopTargetLibDir = $(TopResultDir)/$(TargetTuple)/lib - -ifndef BuildJobs -BuildJobs := $(shell getconf _NPROCESSORS_ONLN) -endif -JOBSFLAG := -j$(BuildJobs) - -# -# configure-arch -# -# Configure OpenThread for the specified architecture. -# -# arch - The architecture to configure. -# -define configure-arch -$(ECHO) " CONFIG $(TargetTuple)..." -(cd $(BuildPath)/$(TargetTuple) && $(AbsTopSourceDir)/configure \ -INSTALL="$(INSTALL) $(INSTALLFLAGS)" \ -CPP="$(CPP)" CC="$(CC)" CXX="$(CXX)" OBJC="$(OBJC)" OBJCXX="$(OBJCXX)" AR="$(AR)" RANLIB="$(RANLIB)" NM="$(NM)" STRIP="$(STRIP)" CPPFLAGS="$(CPPFLAGS)" CFLAGS="$(CFLAGS)" CXXFLAGS="$(CXXFLAGS)" LDFLAGS="$(LDFLAGS)" \ ---host=arm-none-eabi \ ---prefix=/ \ ---exec-prefix=/$(TargetTuple) \ -$(configure_OPTIONS)) -endef # configure-arch - -# -# build-arch -# -# Build the OpenThread intermediate build products for the specified -# architecture. -# -# arch - The architecture to build. -# -define build-arch -$(ECHO) " BUILD $(TargetTuple)" -$(MAKE) $(JOBSFLAG) -C $(BuildPath)/$(TargetTuple) --no-print-directory \ -all -endef # build-arch - -# -# stage-arch -# -# Stage (install) the OpenThread final build products for the specified -# architecture. -# -# arch - The architecture to stage. -# -define stage-arch -$(ECHO) " STAGE $(TargetTuple)" -$(MAKE) $(JOBSFLAG) -C $(BuildPath)/$(TargetTuple) --no-print-directory \ -DESTDIR=$(AbsTopResultDir) \ -install -endef # stage-arch - -# -# ARCH_template -# -# Define macros, targets and rules to configure, build, and stage the -# OpenThread for a single architecture. -# -# arch - The architecture to instantiate the template for. -# -define ARCH_template -CONFIGURE_TARGETS += configure-$(1) -BUILD_TARGETS += do-build-$(1) -STAGE_TARGETS += stage-$(1) -BUILD_DIRS += $(BuildPath)/$(TargetTuple) -DIRECTORIES += $(BuildPath)/$(TargetTuple) - -configure-$(1): target_CPPFLAGS=$($(1)_target_CPPFLAGS) -configure-$(1): target_CFLAGS=$($(1)_target_CFLAGS) -configure-$(1): target_CXXFLAGS=$($(1)_target_CXXFLAGS) -configure-$(1): target_LDFLAGS=$($(1)_target_LDFLAGS) - -configure-$(1): $(BuildPath)/$(TargetTuple)/config.status - -$(BuildPath)/$(TargetTuple)/config.status: | $(BuildPath)/$(TargetTuple) - $$(call configure-arch,$(1)) - -do-build-$(1): configure-$(1) - -do-build-$(1): - +$$(call build-arch,$(1)) - -stage-$(1): do-build-$(1) - -stage-$(1): | $(TopResultDir) - $$(call stage-arch,$(1)) - -$(1): stage-$(1) -endef # ARCH_template - -.DEFAULT_GOAL := all - -all: stage - -# -# cortex-m3 -# - -cortex-m3_target_ABI = cortex-m3 -cortex-m3_target_CPPFLAGS = -mcpu=cortex-m3 -mfloat-abi=soft -mthumb -cortex-m3_target_CFLAGS = -mcpu=cortex-m3 -mfloat-abi=soft -mthumb -cortex-m3_target_CXXFLAGS = -mcpu=cortex-m3 -mfloat-abi=soft -mthumb -cortex-m3_target_LDFLAGS = -mcpu=cortex-m3 -mfloat-abi=soft -mthumb - -# Instantiate an architecture-specific build template for each target -# architecture. - -$(foreach arch,$(ARCHS),$(eval $(call ARCH_template,$(arch)))) - -# -# Common / Finalization -# - -configure: $(CONFIGURE_TARGETS) - -build: $(BUILD_TARGETS) - -stage: $(STAGE_TARGETS) - -DIRECTORIES = $(TopResultDir) $(TopResultDir)/$(TargetTuple)/lib $(BUILD_DIRS) - -CLEAN_DIRS = $(TopResultDir) $(BUILD_DIRS) - -all: stage - -$(DIRECTORIES): - $(ECHO) " MKDIR $@" - @$(MKDIR_P) "$@" - -clean: - $(ECHO) " CLEAN" - @$(RM_F) -r $(CLEAN_DIRS) - -help: - $(ECHO) "Simply type 'make -f $(firstword $(MAKEFILE_LIST))' to build OpenThread for the following " - $(ECHO) "architectures: " - $(ECHO) "" - $(ECHO) " $(ARCHS)" - $(ECHO) "" - $(ECHO) "To build only a particular architecture, specify: " - $(ECHO) "" - $(ECHO) " make -f $(firstword $(MAKEFILE_LIST)) " - $(ECHO) "" diff --git a/examples/apps/cli/main.c b/examples/apps/cli/main.c index 165f32f9395..62b65105d95 100644 --- a/examples/apps/cli/main.c +++ b/examples/apps/cli/main.c @@ -50,12 +50,12 @@ extern void otAppCliInit(otInstance *aInstance); #if OPENTHREAD_CONFIG_HEAP_EXTERNAL_ENABLE -void *otPlatCAlloc(size_t aNum, size_t aSize) +OT_TOOL_WEAK void *otPlatCAlloc(size_t aNum, size_t aSize) { return calloc(aNum, aSize); } -void otPlatFree(void *aPtr) +OT_TOOL_WEAK void otPlatFree(void *aPtr) { free(aPtr); } @@ -67,7 +67,7 @@ void otTaskletsSignalPending(otInstance *aInstance) } #if OPENTHREAD_POSIX && !defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION) -static void ProcessExit(void *aContext, uint8_t aArgsLength, char *aArgs[]) +static otError ProcessExit(void *aContext, uint8_t aArgsLength, char *aArgs[]) { OT_UNUSED_VARIABLE(aContext); OT_UNUSED_VARIABLE(aArgsLength); @@ -75,8 +75,18 @@ static void ProcessExit(void *aContext, uint8_t aArgsLength, char *aArgs[]) exit(EXIT_SUCCESS); } -static const otCliCommand kCommands[] = {{"exit", ProcessExit}}; + +#if OPENTHREAD_EXAMPLES_SIMULATION +extern otError ProcessNodeIdFilter(void *aContext, uint8_t aArgsLength, char *aArgs[]); +#endif + +static const otCliCommand kCommands[] = { + {"exit", ProcessExit}, +#if OPENTHREAD_EXAMPLES_SIMULATION + {"nodeidfilter", ProcessNodeIdFilter}, #endif +}; +#endif // OPENTHREAD_POSIX && !defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION) int main(int argc, char *argv[]) { diff --git a/examples/apps/ncp/main.c b/examples/apps/ncp/main.c index af50d145d0c..548cc69bfa7 100644 --- a/examples/apps/ncp/main.c +++ b/examples/apps/ncp/main.c @@ -46,12 +46,12 @@ extern void otAppNcpInit(otInstance *aInstance); #if OPENTHREAD_CONFIG_HEAP_EXTERNAL_ENABLE -void *otPlatCAlloc(size_t aNum, size_t aSize) +OT_TOOL_WEAK void *otPlatCAlloc(size_t aNum, size_t aSize) { return calloc(aNum, aSize); } -void otPlatFree(void *aPtr) +OT_TOOL_WEAK void otPlatFree(void *aPtr) { free(aPtr); } diff --git a/examples/platforms/Makefile.am b/examples/platforms/Makefile.am index e7bd3ed6c2e..7f86919d855 100644 --- a/examples/platforms/Makefile.am +++ b/examples/platforms/Makefile.am @@ -30,6 +30,7 @@ include $(abs_top_nlbuild_autotools_dir)/automake/pre.am EXTRA_DIST = \ cc1352 \ + cc2538 \ cc2652 \ efr32 \ gp712 \ @@ -45,7 +46,6 @@ EXTRA_DIST = \ # Always package (e.g. for 'make dist') these subdirectories. DIST_SUBDIRS = \ - cc2538 \ simulation \ utils \ $(NULL) @@ -56,10 +56,6 @@ SUBDIRS = \ utils \ $(NULL) -if OPENTHREAD_PLATFORM_CC2538 -SUBDIRS += cc2538 -endif - if OPENTHREAD_PLATFORM_SIMULATION SUBDIRS += simulation endif diff --git a/examples/platforms/Makefile.platform.am b/examples/platforms/Makefile.platform.am index 8ab6520e2d3..71b82b0c2a7 100644 --- a/examples/platforms/Makefile.platform.am +++ b/examples/platforms/Makefile.platform.am @@ -41,10 +41,6 @@ LDFLAGS_COMMON = $(NULL) SOURCES_COMMON = $(NULL) LIBTOOLFLAGS_COMMON = --preserve-dup-deps -if OPENTHREAD_EXAMPLES_CC2538 -include $(top_srcdir)/examples/platforms/cc2538/Makefile.platform.am -endif - if OPENTHREAD_EXAMPLES_SIMULATION include $(top_srcdir)/examples/platforms/simulation/Makefile.platform.am endif diff --git a/examples/platforms/cc2538/CMakeLists.txt b/examples/platforms/cc2538/CMakeLists.txt deleted file mode 100644 index f8ed5f212c5..00000000000 --- a/examples/platforms/cc2538/CMakeLists.txt +++ /dev/null @@ -1,79 +0,0 @@ -# -# Copyright (c) 2019, The OpenThread Authors. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# 3. Neither the name of the copyright holder nor the -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# - -set(OT_PLATFORM_LIB "openthread-cc2538" PARENT_SCOPE) - -if(NOT OT_CONFIG) - set(OT_CONFIG "openthread-core-cc2538-config.h") - set(OT_CONFIG ${OT_CONFIG} PARENT_SCOPE) -endif() - -list(APPEND OT_PLATFORM_DEFINES - "OPENTHREAD_CORE_CONFIG_PLATFORM_CHECK_FILE=\"openthread-core-cc2538-config-check.h\"" - "OPENTHREAD_CONFIG_NCP_HDLC_ENABLE=1" -) -set(OT_PLATFORM_DEFINES ${OT_PLATFORM_DEFINES} PARENT_SCOPE) - -list(APPEND OT_PLATFORM_DEFINES "OPENTHREAD_PROJECT_CORE_CONFIG_FILE=\"${OT_CONFIG}\"") - -add_library(openthread-cc2538 - alarm.c - diag.c - entropy.c - flash.c - misc.c - radio.c - startup-gcc.c - system.c - logging.c - uart.c - $ -) - -target_link_libraries(openthread-cc2538 - PRIVATE - ot-config - PUBLIC - -T${PROJECT_SOURCE_DIR}/examples/platforms/cc2538/cc2538.ld - -Wl,--gc-sections -Wl,-Map=$.map -) - -target_compile_definitions(openthread-cc2538 - PUBLIC - ${OT_PLATFORM_DEFINES} -) - -target_compile_options(openthread-cc2538 PRIVATE - ${OT_CFLAGS} -) - -target_include_directories(openthread-cc2538 PRIVATE - ${OT_PUBLIC_INCLUDES} - ${PROJECT_SOURCE_DIR}/examples/platforms - ${PROJECT_SOURCE_DIR}/src/core -) diff --git a/examples/platforms/cc2538/Makefile.am b/examples/platforms/cc2538/Makefile.am deleted file mode 100644 index fb802f21183..00000000000 --- a/examples/platforms/cc2538/Makefile.am +++ /dev/null @@ -1,69 +0,0 @@ -# -# Copyright (c) 2016, The OpenThread Authors. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# 3. Neither the name of the copyright holder nor the -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# - -include $(abs_top_nlbuild_autotools_dir)/automake/pre.am - -# Do not enable -Wcast-align for this platform -override CFLAGS := $(filter-out -Wcast-align,$(CFLAGS)) -override CXXFLAGS := $(filter-out -Wcast-align,$(CXXFLAGS)) - -lib_LIBRARIES = libopenthread-cc2538.a - -libopenthread_cc2538_a_CPPFLAGS = \ - -I$(top_srcdir)/include \ - -I$(top_srcdir)/examples/platforms \ - -I$(top_srcdir)/src/core \ - $(NULL) - -PLATFORM_SOURCES = \ - alarm.c \ - cc2538-reg.h \ - diag.c \ - entropy.c \ - flash.c \ - misc.c \ - openthread-core-cc2538-config.h \ - openthread-core-cc2538-config-check.h \ - platform-cc2538.h \ - radio.c \ - rom-utility.h \ - startup-gcc.c \ - system.c \ - logging.c \ - uart.c \ - $(NULL) - -libopenthread_cc2538_a_SOURCES = \ - $(PLATFORM_SOURCES) \ - $(NULL) - -Dash = - -libopenthread_cc2538_a_LIBADD = \ - $(shell find $(top_builddir)/examples/platforms/utils $(Dash)type f $(Dash)name "*.o") - -include $(abs_top_nlbuild_autotools_dir)/automake/post.am diff --git a/examples/platforms/cc2538/README.md b/examples/platforms/cc2538/README.md index 91d5b7afbbb..d6674dde27d 100644 --- a/examples/platforms/cc2538/README.md +++ b/examples/platforms/cc2538/README.md @@ -1,112 +1 @@ -# OpenThread on CC2538 Example - -This directory contains example platform drivers for the [Texas Instruments CC2538][cc2538]. - -[cc2538]: http://www.ti.com/product/CC2538 - -The example platform drivers are intended to present the minimal code necessary to support OpenThread. As a result, the example platform drivers do not necessarily highlight the platform's full capabilities. - -## Toolchain - -Download and install the [GNU toolchain for ARM Cortex-M][gnu-toolchain]. - -[gnu-toolchain]: https://developer.arm.com/tools-and-software/open-source-software/developer-tools/gnu-toolchain/gnu-rm - -In a Bash terminal, follow these instructions to install the GNU toolchain and other dependencies. - -```bash -$ cd -$ ./script/bootstrap -``` - -## Building - -In a Bash terminal, follow these instructions to build the cc2538 examples. - -```bash -$ cd -$ ./bootstrap -$ make -f examples/Makefile-cc2538 -``` - -### CC2592 support - -If your board has a CC2592 range extender front-end IC connected to the CC2538 (e.g. the CC2538-CC2592 EM reference design), you need to initialise this part before reception of radio traffic will work. - -Support is enabled in OpenThread by building with `CC2592=1`: - -```bash -$ make -f examples/Makefile-cc2538 CC2592=1 -``` - -The default settings should work for any design following the integration advice given in TI's application report ["AN130 - Using CC2592 Front End With CC2538"](http://www.ti.com/lit/pdf/swra447). - -Additional settings can be customised: - -- `CC2592_PA_EN`: This specifies which pin (on port C of the CC2538) connects to the CC2592's `PA_EN` pin. The default is `3` (PC3). -- `CC2592_LNA_EN`: This specifies which pin (on port C of the CC2538) connects to the CC2592's `LNA_EN` pin. The default is `2` (PC2). -- `CC2592_USE_HGM`: This defines whether the HGM pin of the CC2592 is under GPIO control or not. If not, it is assumed that the HGM pin is tied to a power rail. -- `CC2592_HGM_PORT`: The HGM pin can be connected to any free GPIO. TI recommend using PD2, however if you've used a pin on another GPIO port, you may specify that port (`A`, `B` or `C`) here. -- `CC2592_HGM_PORT`: The HGM pin can be connected to any free GPIO. TI recommend using PD2, however if you've used a pin on another GPIO port, you may specify that port (`A`, `B` or `C`) here. Default is `D`. -- `CC2592_HGM_PIN`: The HGM pin can be connected to any free GPIO. TI recommend using PD2, however if you've used a pin on another GPIO pin, you can specify the pin here. Default is `2`. -- `CC2592_HGM_DEFAULT_STATE`: By default, HGM is enabled at power-on, but you may want to have it default to off, specify `CC2592_HGM_DEFAULT_STATE=0` to do so. -- `CC2538_RECEIVE_SENSITIVITY`: If you have tied the HGM pin to a power rail, this allows you to calibrate the RSSI values according to the new receive sensitivity. This has no effect if `CC2592_USE_HGM=1` (the default). -- `CC2538_RSSI_OFFSET`: If you have tied the HGM pin to a power rail, this allows you to calibrate the RSSI values according to the new RSSI offset. This has no effect if `CC2592_USE_HGM=1` (the default). - -## Flash Binaries - -If the build completed successfully, the `elf` files may be found in `/output/cc2538/bin`. - -To flash the images with [Flash Programmer 2][ti-flash-programmer-2], the files must have the `*.elf` extension. - -```bash -$ cd /output/cc2538/bin -$ cp ot-cli ot-cli.elf -``` - -To load the images with the [serial bootloader][ti-cc2538-bootloader], the images must be converted to `bin`. This is done using `arm-none-eabi-objcopy` - -```bash -$ cd /output/cc2538/bin -$ arm-none-eabi-objcopy -O binary ot-cli ot-cli.bin -``` - -The [cc2538-bsl.py script][cc2538-bsl-tool] provides a convenient method for flashing a CC2538 via the UART. To enter the bootloader backdoor for flashing, hold down SELECT for CC2538DK (corresponds to logic '0') while you press the Reset button. - -[ti-flash-programmer-2]: http://www.ti.com/tool/flash-programmer -[ti-cc2538-bootloader]: http://www.ti.com/lit/an/swra466a/swra466a.pdf -[cc2538-bsl-tool]: https://github.com/JelmerT/cc2538-bsl - -## Interact - -1. Open terminal to `/dev/ttyUSB1` (serial port settings: 115200 8-N-1). -2. Type `help` for list of commands. - -```bash -> help -help -channel -childtimeout -contextreusedelay -extaddr -extpanid -ipaddr -keysequence -leaderweight -mode -netdata register -networkidtimeout -networkkey -networkname -panid -ping -prefix -releaserouterid -rloc16 -route -routerupgradethreshold -scan -start -state -stop -``` +The OpenThread on CC2538 example has moved to https://github.com/openthread/ot-cc2538 diff --git a/examples/platforms/cc2538/alarm.c b/examples/platforms/cc2538/alarm.c deleted file mode 100644 index 7deefda0c79..00000000000 --- a/examples/platforms/cc2538/alarm.c +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Copyright (c) 2016, The OpenThread Authors. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the copyright holder nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -/** - * @file - * This file implements the OpenThread platform abstraction for the alarm. - * - */ - -#include -#include - -#include -#include -#include - -#include "platform-cc2538.h" - -enum -{ - kSystemClock = 32000000, ///< MHz - kTicksPerSec = 1000, ///< Ticks per second -}; - -static uint32_t sCounter = 0; -static uint32_t sAlarmT0 = 0; -static uint32_t sAlarmDt = 0; -static bool sIsRunning = false; - -static uint8_t sTimersIsRunning = 0; -static uint32_t sTimersExpireAt[OT_CC2538_TIMERS_COUNT]; - -extern void cc2538EnergyScanTimerHandler(void); - -void cc2538SetTimer(otCC2538Timer aTimer, uint32_t aDelay) -{ - sTimersIsRunning |= (1 << aTimer); - sTimersExpireAt[aTimer] = sCounter + aDelay; -} - -void cc2538AlarmInit(void) -{ - HWREG(NVIC_ST_RELOAD) = kSystemClock / kTicksPerSec; - HWREG(NVIC_ST_CTRL) = NVIC_ST_CTRL_CLK_SRC | NVIC_ST_CTRL_INTEN | NVIC_ST_CTRL_ENABLE; -} - -uint32_t otPlatAlarmMilliGetNow(void) -{ - return sCounter; -} - -void otPlatAlarmMilliStartAt(otInstance *aInstance, uint32_t t0, uint32_t dt) -{ - OT_UNUSED_VARIABLE(aInstance); - - sAlarmT0 = t0; - sAlarmDt = dt; - sIsRunning = true; -} - -void otPlatAlarmMilliStop(otInstance *aInstance) -{ - OT_UNUSED_VARIABLE(aInstance); - - sIsRunning = false; -} - -void cc2538AlarmProcess(otInstance *aInstance) -{ - uint32_t expires; - bool fire = false; - - if (sTimersIsRunning) - { - if ((int32_t)(sTimersExpireAt[OT_CC2538_TIMER_ENERGY_SCAN] - sCounter) < 0) - { - sTimersIsRunning &= ~(1 << OT_CC2538_TIMER_ENERGY_SCAN); - cc2538EnergyScanTimerHandler(); - } - } - - if (sIsRunning) - { - expires = sAlarmT0 + sAlarmDt; - - if (sAlarmT0 <= sCounter) - { - if (expires >= sAlarmT0 && expires <= sCounter) - { - fire = true; - } - } - else - { - if (expires >= sAlarmT0 || expires <= sCounter) - { - fire = true; - } - } - - if (fire) - { - sIsRunning = false; - -#if OPENTHREAD_CONFIG_DIAG_ENABLE - - if (otPlatDiagModeGet()) - { - otPlatDiagAlarmFired(aInstance); - } - else -#endif - { - otPlatAlarmMilliFired(aInstance); - } - } - } -} - -void SysTick_Handler() -{ - sCounter++; -} diff --git a/examples/platforms/cc2538/arm-none-eabi.cmake b/examples/platforms/cc2538/arm-none-eabi.cmake deleted file mode 100644 index 2e39ec11d47..00000000000 --- a/examples/platforms/cc2538/arm-none-eabi.cmake +++ /dev/null @@ -1,42 +0,0 @@ -# -# Copyright (c) 2019, The OpenThread Authors. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# 3. Neither the name of the copyright holder nor the -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# - -set(CMAKE_SYSTEM_NAME Generic) -set(CMAKE_SYSTEM_PROCESSOR ARM) - -set(CMAKE_C_COMPILER arm-none-eabi-gcc) -set(CMAKE_CXX_COMPILER arm-none-eabi-g++) -set(CMAKE_ASM_COMPILER arm-none-eabi-as) -set(CMAKE_RANLIB arm-none-eabi-ranlib) - -set(COMMON_C_FLAGS "-mthumb -fno-builtin -Wall -fdata-sections -ffunction-sections -mabi=aapcs -mcpu=cortex-m3 -mfloat-abi=soft") - -set(CMAKE_C_FLAGS_INIT "${COMMON_C_FLAGS} -std=gnu99") -set(CMAKE_CXX_FLAGS_INIT "${COMMON_C_FLAGS} -fno-exceptions -fno-rtti") -set(CMAKE_ASM_FLAGS_INIT "${COMMON_C_FLAGS}") -set(CMAKE_EXE_LINKER_FLAGS_INIT "${COMMON_C_FLAGS} -specs=nano.specs -specs=nosys.specs -nostartfiles") diff --git a/examples/platforms/cc2538/cc2538-reg.h b/examples/platforms/cc2538/cc2538-reg.h deleted file mode 100644 index 54972fbeabe..00000000000 --- a/examples/platforms/cc2538/cc2538-reg.h +++ /dev/null @@ -1,326 +0,0 @@ -/* - * Copyright (c) 2016, The OpenThread Authors. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the copyright holder nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -/** - * @file - * This file includes CC2538 register definitions. - * - */ - -#ifndef CC2538_REG_H_ -#define CC2538_REG_H_ - -#include - -// clang-format off - -#define HWREG(x) (*((volatile uint32_t *)(x))) - -/*! - * For registers that are arrays of 32-bit integers. - * - * @param reg Register address - * @param idx Register array index - */ -#define HWREG_ARR(reg, idx) HWREG((reg) + ((idx) << 2)) - -#define NVIC_ST_CTRL 0xE000E010 // SysTick Control and Status -#define NVIC_ST_RELOAD 0xE000E014 // SysTick Reload Value Register -#define NVIC_EN0 0xE000E100 // Interrupt 0-31 Set Enable - -#define NVIC_ST_CTRL_COUNT 0x00010000 // Count Flag -#define NVIC_ST_CTRL_CLK_SRC 0x00000004 // Clock Source -#define NVIC_ST_CTRL_INTEN 0x00000002 // Interrupt Enable -#define NVIC_ST_CTRL_ENABLE 0x00000001 // Enable - -#define RFCORE_XREG_SRCMATCH_EN 0x00000001 // SRCMATCH.SRC_MATCH_EN(1) -#define RFCORE_XREG_SRCMATCH_AUTOPEND 0x00000002 // SRCMATCH.AUTOPEND(1) -#define RFCORE_XREG_SRCMATCH_PEND_DATAREQ_ONLY 0x00000004 // SRCMATCH.PEND_DATAREQ_ONLY(1) - -#define RFCORE_XREG_SRCMATCH_ENABLE_STATUS_SIZE 3 // Num of register for source match enable status -#define RFCORE_XREG_SRCMATCH_SHORT_ENTRIES 24 // 24 short address entries in maximum -#define RFCORE_XREG_SRCMATCH_EXT_ENTRIES 12 // 12 extended address entries in maximum -#define RFCORE_XREG_SRCMATCH_SHORT_ENTRY_OFFSET 4 // address offset for one short address entry -#define RFCORE_XREG_SRCMATCH_EXT_ENTRY_OFFSET 8 // address offset for one extended address entry - -#define INT_UART0 21 // UART0 Rx and Tx - -#define IEEE_EUI64 0x00280028 // Address of IEEE EUI-64 address - -#define RFCORE_FFSM_SRCADDRESS_TABLE 0x40088400 // Source Address Table - -#define RFCORE_FFSM_SRCEXTPENDEN0 0x40088590 // Enable/Disable automatic pending per extended address -#define RFCORE_FFSM_SRCSHORTPENDEN0 0x4008859C // Enable/Disable automatic pending per short address -#define RFCORE_FFSM_EXT_ADDR0 0x400885A8 // Local address information -#define RFCORE_FFSM_PAN_ID0 0x400885C8 // Local address information -#define RFCORE_FFSM_PAN_ID1 0x400885CC // Local address information -#define RFCORE_FFSM_SHORT_ADDR0 0x400885D0 // Local address information -#define RFCORE_FFSM_SHORT_ADDR1 0x400885D4 // Local address information -#define RFCORE_XREG_FRMFILT0 0x40088600 // The frame filtering function -#define RFCORE_XREG_SRCMATCH 0x40088608 // Source address matching and pending bits -#define RFCORE_XREG_SRCSHORTEN0 0x4008860C // Short address matching -#define RFCORE_XREG_SRCEXTEN0 0x40088618 // Extended address matching - -#define RFCORE_XREG_FRMCTRL0 0x40088624 // Frame handling -#define RFCORE_XREG_FRMCTRL1 0x40088628 // Frame handling -#define RFCORE_XREG_RXENABLE 0x4008862C // RX enabling -#define RFCORE_XREG_FREQCTRL 0x4008863C // Controls the RF frequency -#define RFCORE_XREG_TXPOWER 0x40088640 // Controls the output power -#define RFCORE_XREG_FSMSTAT0 0x40088648 // Radio finite state machine status -#define RFCORE_XREG_FSMSTAT1 0x4008864C // Radio status register -#define RFCORE_XREG_FIFOPCTRL 0x40088650 // FIFOP threshold -#define RFCORE_XREG_CCACTRL0 0x40088658 // CCA threshold -#define RFCORE_XREG_RSSI 0x40088660 // RSSI status register -#define RFCORE_XREG_RSSISTAT 0x40088664 // RSSI valid status register -#define RFCORE_XREG_AGCCTRL1 0x400886C8 // AGC reference level -#define RFCORE_XREG_RFC_OBS_CTRL 0x400887AC // RF Core observable output -#define RFCORE_XREG_TXFILTCFG 0x400887E8 // TX filter configuration -#define RFCORE_XREG_RFRND 0x4008869C // Random data -#define RFCORE_SFR_RFDATA 0x40088828 // The TX FIFO and RX FIFO -#define RFCORE_SFR_RFERRF 0x4008882C // RF error interrupt flags -#define RFCORE_SFR_RFIRQF0 0x40088834 // RF interrupt flags -#define RFCORE_SFR_RFST 0x40088838 // RF CSMA-CA/strobe processor -#define CCTEST_OBSSEL 0x44010014 // CCTEST observable output route - -#define RFCORE_XREG_FRMFILT0_FRAME_FILTER_EN 0x00000001 // Enables frame filtering - -#define RFCORE_XREG_FRMCTRL0_AUTOACK 0x00000020 -#define RFCORE_XREG_FRMCTRL0_ENERGY_SCAN 0x00000010 -#define RFCORE_XREG_FRMCTRL0_AUTOCRC 0x00000040 -#define RFCORE_XREG_FRMCTRL0_INFINITY_RX 0x00000008 - -#define RFCORE_XREG_FRMCTRL1_PENDING_OR 0x00000004 - -#define RFCORE_XREG_RFRND_IRND 0x00000001 - -#define RFCORE_XREG_FSMSTAT0_STATE_MASK 0x0000003F -#define RFCORE_XREG_FSMSTAT0_CAL_DONE 0x00000080 -#define RFCORE_XREG_FSMSTAT0_CAL_RUN 0x00000040 - -#define RFCORE_XREG_FSMSTAT0_STATE_IDLE 0x00000000 -#define RFCORE_XREG_FSMSTAT0_STATE_RX_CAL 0x00000002 -#define RFCORE_XREG_FSMSTAT0_STATE_SFD_WAIT0 0x00000003 -#define RFCORE_XREG_FSMSTAT0_STATE_SFD_WAIT1 0x00000004 -#define RFCORE_XREG_FSMSTAT0_STATE_SFD_WAIT2 0x00000005 -#define RFCORE_XREG_FSMSTAT0_STATE_SFD_WAIT3 0x00000006 -#define RFCORE_XREG_FSMSTAT0_STATE_RX0 0x00000007 -#define RFCORE_XREG_FSMSTAT0_STATE_RX1 0x00000008 -#define RFCORE_XREG_FSMSTAT0_STATE_RX2 0x00000009 -#define RFCORE_XREG_FSMSTAT0_STATE_RX3 0x0000000A -#define RFCORE_XREG_FSMSTAT0_STATE_RX4 0x0000000B -#define RFCORE_XREG_FSMSTAT0_STATE_RX5 0x0000000C -#define RFCORE_XREG_FSMSTAT0_STATE_RX6 0x0000000D -#define RFCORE_XREG_FSMSTAT0_STATE_RX_WAIT 0x0000000E -#define RFCORE_XREG_FSMSTAT0_STATE_RX_FRST 0x00000010 -#define RFCORE_XREG_FSMSTAT0_STATE_RX_OVER 0x00000011 -#define RFCORE_XREG_FSMSTAT0_STATE_TX_CAL 0x00000020 -#define RFCORE_XREG_FSMSTAT0_STATE_TX0 0x00000022 -#define RFCORE_XREG_FSMSTAT0_STATE_TX1 0x00000023 -#define RFCORE_XREG_FSMSTAT0_STATE_TX2 0x00000024 -#define RFCORE_XREG_FSMSTAT0_STATE_TX3 0x00000025 -#define RFCORE_XREG_FSMSTAT0_STATE_TX4 0x00000026 -#define RFCORE_XREG_FSMSTAT0_STATE_TX_FINAL 0x00000027 -#define RFCORE_XREG_FSMSTAT0_STATE_RXTX_TRANS 0x00000028 -#define RFCORE_XREG_FSMSTAT0_STATE_ACK_CAL 0x00000030 -#define RFCORE_XREG_FSMSTAT0_STATE_ACK0 0x00000031 -#define RFCORE_XREG_FSMSTAT0_STATE_ACK1 0x00000032 -#define RFCORE_XREG_FSMSTAT0_STATE_ACK2 0x00000033 -#define RFCORE_XREG_FSMSTAT0_STATE_ACK3 0x00000034 -#define RFCORE_XREG_FSMSTAT0_STATE_ACK4 0x00000035 -#define RFCORE_XREG_FSMSTAT0_STATE_ACK5 0x00000036 -#define RFCORE_XREG_FSMSTAT0_STATE_ACK_DELAY 0x00000037 -#define RFCORE_XREG_FSMSTAT0_STATE_TX_UNDER 0x00000038 -#define RFCORE_XREG_FSMSTAT0_STATE_TX_DOWN0 0x0000001A -#define RFCORE_XREG_FSMSTAT0_STATE_TX_DOWN1 0x0000003A - -#define RFCORE_XREG_FSMSTAT1_RX_ACTIVE 0x00000001 -#define RFCORE_XREG_FSMSTAT1_TX_ACTIVE 0x00000002 -#define RFCORE_XREG_FSMSTAT1_LOCK_STATUS 0x00000004 -#define RFCORE_XREG_FSMSTAT1_SAMPLED_CCA 0x00000008 -#define RFCORE_XREG_FSMSTAT1_CCA 0x00000010 // Clear channel assessment -#define RFCORE_XREG_FSMSTAT1_SFD 0x00000020 -#define RFCORE_XREG_FSMSTAT1_FIFOP 0x00000040 -#define RFCORE_XREG_FSMSTAT1_FIFO 0x00000080 - -#define RFCORE_XREG_RSSISTAT_RSSI_VALID 0x00000001 // RSSI value is valid. - -#define RFCORE_XREG_RFC_OBS_POL_INV 0x00000040 // Invert polarity of OBS signal -#define RFCORE_XREG_RFC_OBS_MUX_ZERO 0x00000000 // Observable = constant zero -#define RFCORE_XREG_RFC_OBS_MUX_ONE 0x00000001 // Observable = constant one -#define RFCORE_XREG_RFC_OBS_MUX_SNIFF_DATA 0x00000008 // RFC sniff data -#define RFCORE_XREG_RFC_OBS_MUX_SNIFF_CLK 0x00000009 // RFC sniff clock -#define RFCORE_XREG_RFC_OBS_MUX_RSSI_VALID 0x0000000c // RSSI valid -#define RFCORE_XREG_RFC_OBS_MUX_DEMOD_CCA 0x0000000d // Clear channel assessment -#define RFCORE_XREG_RFC_OBS_MUX_SAMPLED_CCA 0x0000000e // Sampled CCA signal -#define RFCORE_XREG_RFC_OBS_MUX_SFD_SYNC 0x0000000f // SFD received or transmitted -#define RFCORE_XREG_RFC_OBS_MUX_TX_ACTIVE 0x00000010 // Transmitter is active -#define RFCORE_XREG_RFC_OBS_MUX_RX_ACTIVE 0x00000011 // Receiver is active -#define RFCORE_XREG_RFC_OBS_MUX_FFCTRL_FIFO 0x00000012 // One or more bytes in FIFO -#define RFCORE_XREG_RFC_OBS_MUX_FFCTRL_FIFOP 0x00000013 // One or more frames in FIFO -#define RFCORE_XREG_RFC_OBS_MUX_PACKET_DONE 0x00000014 // Packet received -#define RFCORE_XREG_RFC_OBS_MUX_RFC_XOR_RAND_IQ 0x00000016 // RAND I ^ RAND Q -#define RFCORE_XREG_RFC_OBS_MUX_RFC_RAND_Q 0x00000017 // Random data from Q channel -#define RFCORE_XREG_RFC_OBS_MUX_RFC_RAND_I 0x00000018 // Random data from I channel -#define RFCORE_XREG_RFC_OBS_MUX_LOCK_STATUS 0x00000019 // PLL is in lock -#define RFCORE_XREG_RFC_OBS_MUX_PA_PD 0x00000028 // Power amp power down -#define RFCORE_XREG_RFC_OBS_MUX_LNA_PD 0x0000002a // LNA power down - -#define RFCORE_SFR_RFERRF_NLOCK 0x00000001 // Failed to achieve PLL lock. -#define RFCORE_SFR_RFERRF_RXABO 0x00000002 // RX Aborted. -#define RFCORE_SFR_RFERRF_RXOVERF 0x00000004 // RX FIFO overflowed. -#define RFCORE_SFR_RFERRF_RXUNDERF 0x00000008 // RX FIFO underflowed. -#define RFCORE_SFR_RFERRF_TXOVERF 0x00000010 // TX FIFO overflowed. -#define RFCORE_SFR_RFERRF_TXUNDERF 0x00000020 // TX FIFO underflowed. -#define RFCORE_SFR_RFERRF_STROBEERR 0x00000040 // Command Strobe Error. - -#define RFCORE_SFR_RFST_INSTR_RXON 0xE3 // Instruction set RX on -#define RFCORE_SFR_RFST_INSTR_TXON 0xE9 // Instruction set TX on -#define RFCORE_SFR_RFST_INSTR_RFOFF 0xEF // Instruction set RF off -#define RFCORE_SFR_RFST_INSTR_FLUSHRX 0xED // Instruction set flush rx buffer -#define RFCORE_SFR_RFST_INSTR_FLUSHTX 0xEE // Instruction set flush tx buffer - -#define CCTEST_OBSSEL_EN 0x00000080 // Enable the OBS output on this pin -#define CCTEST_OBSSEL_SEL_OBS0 0x00000000 // Route OBS0 to pin -#define CCTEST_OBSSEL_SEL_OBS1 0x00000001 // Route OBS1 to pin -#define CCTEST_OBSSEL_SEL_OBS2 0x00000002 // Route OBS2 to pin - -#define ANA_REGS_BASE 0x400D6000 // ANA_REGS -#define ANA_REGS_O_IVCTRL 0x00000004 // Analog control register - -#define SYS_CTRL_CLOCK_CTRL 0x400D2000 // The clock control register -#define SYS_CTRL_SYSDIV_32MHZ 0x00000000 // Sys_div for sysclk 32MHz -#define SYS_CTRL_CLOCK_CTRL_AMP_DET 0x00200000 - -#define SYS_CTRL_PWRDBG 0x400D2074 -#define SYS_CTRL_PWRDBG_FORCE_WARM_RESET 0x00000008 - -#define SYS_CTRL_RCGCUART 0x400D2028 -#define SYS_CTRL_SCGCUART 0x400D202C -#define SYS_CTRL_DCGCUART 0x400D2030 -#define SYS_CTRL_I_MAP 0x400D2098 -#define SYS_CTRL_RCGCRFC 0x400D20A8 -#define SYS_CTRL_SCGCRFC 0x400D20AC -#define SYS_CTRL_DCGCRFC 0x400D20B0 -#define SYS_CTRL_EMUOVR 0x400D20B4 - -#define SYS_CTRL_RCGCRFC_RFC0 0x00000001 -#define SYS_CTRL_SCGCRFC_RFC0 0x00000001 -#define SYS_CTRL_DCGCRFC_RFC0 0x00000001 - -#define SYS_CTRL_I_MAP_ALTMAP 0x00000001 - -#define SYS_CTRL_RCGCUART_UART0 0x00000001 -#define SYS_CTRL_SCGCUART_UART0 0x00000001 -#define SYS_CTRL_DCGCUART_UART0 0x00000001 - -#define SYS_CTRL_RCGCUART_UART1 0x00000002 -#define SYS_CTRL_SCGCUART_UART1 0x00000002 -#define SYS_CTRL_DCGCUART_UART1 0x00000002 - -#define IOC_PA0_SEL 0x400D4000 // Peripheral select control -#define IOC_PA1_SEL 0x400D4004 // Peripheral select control -#define IOC_PA2_SEL 0x400D4008 -#define IOC_PA3_SEL 0x400D400C -#define IOC_UARTRXD_UART0 0x400D4100 -#define IOC_UARTRXD_UART1 0x400D4108 - -#define IOC_PA0_OVER 0x400D4080 -#define IOC_PA1_OVER 0x400D4084 -#define IOC_PA2_OVER 0x400D4088 -#define IOC_PA3_OVER 0x400D408C - -#define IOC_MUX_OUT_SEL_UART0_TXD 0x00000000 -#define IOC_MUX_OUT_SEL_UART1_TXD 0x00000002 - -#define IOC_OVERRIDE_OE 0x00000008 // PAD Config Override Output Enable -#define IOC_OVERRIDE_DIS 0x00000000 // PAD Config Override Disabled - -#define IOC_PAD_IN_SEL_PA0 0x00000000 // PA0 -#define IOC_PAD_IN_SEL_PA1 0x00000001 // PA1 -#define IOC_PAD_IN_SEL_PA2 0x00000002 // PA2 -#define IOC_PAD_IN_SEL_PA3 0x00000003 // PA3 - -#define UART0_BASE 0x4000C000 -#define UART1_BASE 0x4000D000 -#define GPIO_A_BASE 0x400D9000 // GPIO A -#define GPIO_B_BASE 0x400DA000 // GPIO B -#define GPIO_C_BASE 0x400DB000 // GPIO C -#define GPIO_D_BASE 0x400DC000 // GPIO D - -#define GPIO_O_DIR 0x00000400 -#define GPIO_O_AFSEL 0x00000420 - -#define GPIO_PIN(x) (1UL << x) // Arbitrary GPIO pin -#define GPIO_PIN_0 0x00000001 // GPIO pin 0 -#define GPIO_PIN_1 0x00000002 // GPIO pin 1 -#define GPIO_PIN_2 0x00000004 // GPIO pin 2 -#define GPIO_PIN_3 0x00000008 // GPIO pin 3 -#define GPIO_PIN_4 0x00000010 // GPIO pin 4 -#define GPIO_PIN_5 0x00000020 // GPIO pin 5 -#define GPIO_PIN_6 0x00000040 // GPIO pin 6 -#define GPIO_PIN_7 0x00000080 // GPIO pin 7 - -#define UART_O_DR 0x00000000 // UART data -#define UART_O_FR 0x00000018 // UART flag -#define UART_O_IBRD 0x00000024 -#define UART_O_FBRD 0x00000028 -#define UART_O_LCRH 0x0000002C -#define UART_O_CTL 0x00000030 // UART control -#define UART_O_IM 0x00000038 // UART interrupt mask -#define UART_O_MIS 0x00000040 // UART masked interrupt status -#define UART_O_ICR 0x00000044 // UART interrupt clear -#define UART_O_CC 0x00000FC8 // UART clock configuration - -#define UART_FR_RXFE 0x00000010 // UART receive FIFO empty -#define UART_FR_TXFF 0x00000020 // UART transmit FIFO full -#define UART_FR_RXFF 0x00000040 // UART receive FIFO full - -#define UART_CONFIG_WLEN_8 0x00000060 // 8 bit data -#define UART_CONFIG_STOP_ONE 0x00000000 // One stop bit -#define UART_CONFIG_PAR_NONE 0x00000000 // No parity - -#define UART_CTL_UARTEN 0x00000001 // UART enable -#define UART_CTL_TXE 0x00000100 // UART transmit enable -#define UART_CTL_RXE 0x00000200 // UART receive enable - -#define UART_IM_RXIM 0x00000010 // UART receive interrupt mask -#define UART_IM_RTIM 0x00000040 // UART receive time-out interrupt - -#define SOC_ADC_ADCCON1 0x400D7000 // ADC Control -#define SOC_ADC_RNDL 0x400D7014 // RNG low data -#define SOC_ADC_RNDH 0x400D7018 // RNG high data - -#define SOC_ADC_ADCCON1_RCTRL0 0x00000004 // ADCCON1 RCTRL bit 0 -#define SOC_ADC_ADCCON1_RCTRL1 0x00000008 // ADCCON1 RCTRL bit 1 - -#define FLASH_CTRL_FCTL 0x400D3008 // Flash control -#define FLASH_CTRL_DIECFG0 0x400D3014 // Flash information - -// clang-format on - -#endif diff --git a/examples/platforms/cc2538/cc2538.ld b/examples/platforms/cc2538/cc2538.ld deleted file mode 100644 index daff1621f24..00000000000 --- a/examples/platforms/cc2538/cc2538.ld +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Copyright (c) 2016, The OpenThread Authors. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the copyright holder nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -/** - * @file - * GCC linker script for CC2538. - */ - -_512k_bytes = (512*1024); -_256k_bytes = (256*1024); -_128k_bytes = (128*1024); -_FLASH_page_size = 2048; - -/* - * Change for your chip, default is 512k chips - */ -_FLASH_size_bytes = _512k_bytes; -_FLASH_n_pages = (_FLASH_size_bytes / _FLASH_page_size); -/* reduce the usable size by: the CCA + settings Page A & B, total 3 pages */ -_FLASH_usable_size = (_FLASH_size_bytes - (3 * _FLASH_page_size)); -_FLASH_start = 0x00200000; -_FLASH_end = (_FLASH_start + _FLASH_size_bytes); - -/* - * The CCA (Customer Configuration Area) is always the last page. - * See: http://www.ti.com/lit/ug/swru319c/swru319c.pdf - * table 8-2 for more details. - */ -_FLASH_cca_page = (_FLASH_end - (1 * _FLASH_page_size)); - -/* - * OpenThread NV storage goes in the settings page. - * OpenThread requires at least 2 adjacent pages, call them A and B. - */ -_FLASH_settings_pageB = (_FLASH_end - (2 * _FLASH_page_size)); -_FLASH_settings_pageA = (_FLASH_end - (3 * _FLASH_page_size)); - -MEMORY -{ - /* would like to use SYMBOLS (from above)here but we cannot - * GCC version 4.9 does not support symbolic expressions here. - * But later versions do support the feature. - */ - FLASH (rx) : ORIGIN = 0x00200000, LENGTH = 0x0007c000 - FLASH_CCA (rx) : ORIGIN = 0x0027FFD4, LENGTH = 0x2c - SRAM (rwx) : ORIGIN = 0x20000000, LENGTH = 32K -} -/* - * To safty check what would have been the SYMBOL values - * we use these ASSERTS to verify things are still good. - */ -ASSERT( _FLASH_start == 0x00200000, "invalid flash start address for cc2538") -ASSERT( _FLASH_cca_page == 0x0027f800, "invalid cca start address for cc2538") -ASSERT( _FLASH_usable_size == 0x0007e800, "Invalid usable size for this config") - - - -ENTRY(flash_cca_lock_page) -SECTIONS -{ - .text : ALIGN(4) - { - _text = .; - *(.vectors) - *(.text*) - *(.rodata*) - KEEP(*(.init)) - KEEP(*(.fini)) - _etext = .; - } > FLASH= 0 - - .init_array : - { - _init_array = .; - KEEP(*(SORT(.init_array.*))) - KEEP(*(.init_array*)) - _einit_array = .; - } > FLASH - - .ARM.exidx : ALIGN(4) - { - *(.ARM.exidx*) - } > FLASH - - .data : ALIGN(4) - { - _data = .; - *(.data*) - _edata = .; - } > SRAM AT > FLASH - _ldata = LOADADDR(.data); - - .bss : ALIGN(4) - { - _bss = .; - *(.bss*) - *(COMMON) - _ebss = .; - } > SRAM - - _heap = .; - end = .; - - .stack : ALIGN(4) - { - *(.stack) - } > SRAM - - .flashcca : - { - KEEP(*(.flash_cca)) - } > FLASH_CCA -} diff --git a/examples/platforms/cc2538/diag.c b/examples/platforms/cc2538/diag.c deleted file mode 100644 index 579c1e7948c..00000000000 --- a/examples/platforms/cc2538/diag.c +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (c) 2016, The OpenThread Authors. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the copyright holder nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#include -#include -#include -#include - -#include -#include -#include - -#include "platform-cc2538.h" - -#if OPENTHREAD_CONFIG_DIAG_ENABLE - -/** - * Diagnostics mode variables. - * - */ -static bool sDiagMode = false; - -void otPlatDiagModeSet(bool aMode) -{ - sDiagMode = aMode; -} - -bool otPlatDiagModeGet() -{ - return sDiagMode; -} - -void otPlatDiagChannelSet(uint8_t aChannel) -{ - OT_UNUSED_VARIABLE(aChannel); -} - -void otPlatDiagTxPowerSet(int8_t aTxPower) -{ - OT_UNUSED_VARIABLE(aTxPower); -} - -void otPlatDiagRadioReceived(otInstance *aInstance, otRadioFrame *aFrame, otError aError) -{ - OT_UNUSED_VARIABLE(aInstance); - OT_UNUSED_VARIABLE(aFrame); - OT_UNUSED_VARIABLE(aError); -} - -void otPlatDiagAlarmCallback(otInstance *aInstance) -{ - OT_UNUSED_VARIABLE(aInstance); -} - -#endif // OPENTHREAD_CONFIG_DIAG_ENABLE diff --git a/examples/platforms/cc2538/entropy.c b/examples/platforms/cc2538/entropy.c deleted file mode 100644 index 47a31be04d4..00000000000 --- a/examples/platforms/cc2538/entropy.c +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright (c) 2019, The OpenThread Authors. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the copyright holder nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -/** - * @file - * This file implements an entropy source based on ADC. - * - */ - -#include - -#include - -#include "platform-cc2538.h" -#include "utils/code_utils.h" - -static void generateRandom(uint8_t *aOutput, uint16_t aOutputLength) -{ - uint32_t frmctrl0; - - HWREG(SOC_ADC_ADCCON1) &= ~(SOC_ADC_ADCCON1_RCTRL1 | SOC_ADC_ADCCON1_RCTRL0); - HWREG(SYS_CTRL_RCGCRFC) = SYS_CTRL_RCGCRFC_RFC0; - - while (HWREG(SYS_CTRL_RCGCRFC) != SYS_CTRL_RCGCRFC_RFC0) - ; - - frmctrl0 = HWREG(RFCORE_XREG_FRMCTRL0); - HWREG(RFCORE_XREG_FRMCTRL0) = RFCORE_XREG_FRMCTRL0_INFINITY_RX; - HWREG(RFCORE_SFR_RFST) = RFCORE_SFR_RFST_INSTR_RXON; - - while (!HWREG(RFCORE_XREG_RSSISTAT) & RFCORE_XREG_RSSISTAT_RSSI_VALID) - ; - - for (uint16_t index = 0; index < aOutputLength; index++) - { - aOutput[index] = 0; - - for (uint8_t offset = 0; offset < 8 * sizeof(uint8_t); offset++) - { - aOutput[index] <<= 1; - aOutput[index] |= (HWREG(RFCORE_XREG_RFRND) & RFCORE_XREG_RFRND_IRND); - } - } - - HWREG(RFCORE_SFR_RFST) = RFCORE_SFR_RFST_INSTR_RFOFF; - HWREG(RFCORE_XREG_FRMCTRL0) = frmctrl0; -} - -void cc2538RandomInit(void) -{ - uint16_t seed = 0; - - while (seed == 0x0000 || seed == 0x8003) - { - generateRandom((uint8_t *)&seed, sizeof(seed)); - } - - HWREG(SOC_ADC_RNDL) = (seed >> 8) & 0xff; - HWREG(SOC_ADC_RNDL) = seed & 0xff; -} - -otError otPlatEntropyGet(uint8_t *aOutput, uint16_t aOutputLength) -{ - otError error = OT_ERROR_NONE; - uint8_t channel = 0; - - otEXPECT_ACTION(aOutput, error = OT_ERROR_INVALID_ARGS); - - if (sInstance && otPlatRadioIsEnabled(sInstance)) - { - channel = 11 + (HWREG(RFCORE_XREG_FREQCTRL) - 11) / 5; - otPlatRadioSleep(sInstance); - otPlatRadioDisable(sInstance); - } - - generateRandom(aOutput, aOutputLength); - - if (channel) - { - cc2538RadioInit(); - otPlatRadioEnable(sInstance); - otPlatRadioReceive(sInstance, channel); - } - -exit: - return error; -} diff --git a/examples/platforms/cc2538/flash.c b/examples/platforms/cc2538/flash.c deleted file mode 100644 index 04c8a655941..00000000000 --- a/examples/platforms/cc2538/flash.c +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright (c) 2016, The OpenThread Authors. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the copyright holder nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#include -#include -#include - -#include "platform-cc2538.h" -#include "rom-utility.h" - -#define FLASH_CTRL_FCTL_BUSY 0x00000080 - -#define FLASH_PAGE_SIZE 2048 -#define FLASH_PAGE_NUM 2 -#define FLASH_SWAP_SIZE (FLASH_PAGE_SIZE * (FLASH_PAGE_NUM / 2)) - -/* The linker script creates this external symbol */ -extern uint8_t _FLASH_settings_pageA[]; - -/* Convert a settings offset to the physical address within the flash settings pages */ -static uint32_t flashPhysAddr(uint8_t aSwapIndex, uint32_t aOffset) -{ - uint32_t address = (uint32_t)(&_FLASH_settings_pageA[0]) + aOffset; - - if (aSwapIndex) - { - address += FLASH_SWAP_SIZE; - } - - return address; -} - -void otPlatFlashInit(otInstance *aInstance) -{ - OT_UNUSED_VARIABLE(aInstance); -} - -uint32_t otPlatFlashGetSwapSize(otInstance *aInstance) -{ - OT_UNUSED_VARIABLE(aInstance); - - return FLASH_SWAP_SIZE; -} - -void otPlatFlashErase(otInstance *aInstance, uint8_t aSwapIndex) -{ - OT_UNUSED_VARIABLE(aInstance); - - ROM_PageErase(flashPhysAddr(aSwapIndex, 0), FLASH_PAGE_SIZE); - while (HWREG(FLASH_CTRL_FCTL) & FLASH_CTRL_FCTL_BUSY) - { - } -} - -void otPlatFlashWrite(otInstance *aInstance, uint8_t aSwapIndex, uint32_t aOffset, const void *aData, uint32_t aSize) -{ - OT_UNUSED_VARIABLE(aInstance); - - uint32_t *data = (uint32_t *)(aData); - - for (uint32_t size = 0; size < aSize; size += sizeof(uint32_t), aOffset += sizeof(uint32_t), data++) - { - ROM_ProgramFlash(data, flashPhysAddr(aSwapIndex, aOffset), sizeof(uint32_t)); - - while (HWREG(FLASH_CTRL_FCTL) & FLASH_CTRL_FCTL_BUSY) - { - } - } -} - -void otPlatFlashRead(otInstance *aInstance, uint8_t aSwapIndex, uint32_t aOffset, uint8_t *aData, uint32_t aSize) -{ - OT_UNUSED_VARIABLE(aInstance); - - memcpy(aData, (void *)flashPhysAddr(aSwapIndex, aOffset), aSize); -} diff --git a/examples/platforms/cc2538/logging.c b/examples/platforms/cc2538/logging.c deleted file mode 100644 index 82158aeed3a..00000000000 --- a/examples/platforms/cc2538/logging.c +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (c) 2016, The OpenThread Authors. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the copyright holder nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -/** - * @file logging.c - * Platform abstraction for the logging - * - */ - -#include -#include -#include -#include - -#if (OPENTHREAD_CONFIG_LOG_OUTPUT == OPENTHREAD_CONFIG_LOG_OUTPUT_PLATFORM_DEFINED) -OT_TOOL_WEAK void otPlatLog(otLogLevel aLogLevel, otLogRegion aLogRegion, const char *aFormat, ...) -{ - OT_UNUSED_VARIABLE(aLogLevel); - OT_UNUSED_VARIABLE(aLogRegion); - OT_UNUSED_VARIABLE(aFormat); -} -#endif diff --git a/examples/platforms/cc2538/misc.c b/examples/platforms/cc2538/misc.c deleted file mode 100644 index 94dca3dd010..00000000000 --- a/examples/platforms/cc2538/misc.c +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (c) 2016, The OpenThread Authors. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the copyright holder nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#include - -#include "platform-cc2538.h" - -void otPlatReset(otInstance *aInstance) -{ - OT_UNUSED_VARIABLE(aInstance); - HWREG(SYS_CTRL_PWRDBG) = SYS_CTRL_PWRDBG_FORCE_WARM_RESET; -} - -otPlatResetReason otPlatGetResetReason(otInstance *aInstance) -{ - OT_UNUSED_VARIABLE(aInstance); - // TODO: Write me! - return OT_PLAT_RESET_REASON_POWER_ON; -} - -void otPlatWakeHost(void) -{ - // TODO: implement an operation to wake the host from sleep state. -} diff --git a/examples/platforms/cc2538/openthread-core-cc2538-config-check.h b/examples/platforms/cc2538/openthread-core-cc2538-config-check.h deleted file mode 100644 index 93788b12165..00000000000 --- a/examples/platforms/cc2538/openthread-core-cc2538-config-check.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2019, The OpenThread Authors. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the copyright holder nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef OPENTHREAD_CORE_CC2538_CONFIG_CHECK_H_ -#define OPENTHREAD_CORE_CC2538_CONFIG_CHECK_H_ - -#if OPENTHREAD_CONFIG_RADIO_915MHZ_OQPSK_SUPPORT -#error "Platform cc2538 doesn't support configuration option: OPENTHREAD_CONFIG_RADIO_915MHZ_OQPSK_SUPPORT" -#endif - -#endif /* OPENTHREAD_CORE_CC2538_CONFIG_CHECK_H_ */ diff --git a/examples/platforms/cc2538/openthread-core-cc2538-config.h b/examples/platforms/cc2538/openthread-core-cc2538-config.h deleted file mode 100644 index 1f262173e18..00000000000 --- a/examples/platforms/cc2538/openthread-core-cc2538-config.h +++ /dev/null @@ -1,255 +0,0 @@ -/* - * Copyright (c) 2016, The OpenThread Authors. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the copyright holder nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -/** - * @file - * This file includes cc2538 compile-time configuration constants for OpenThread. - */ - -#ifndef OPENTHREAD_CORE_CC2538_CONFIG_H_ -#define OPENTHREAD_CORE_CC2538_CONFIG_H_ - -/** - * @def OPENTHREAD_CONFIG_PLATFORM_INFO - * - * The platform-specific string to insert into the OpenThread version string. - * - */ -#define OPENTHREAD_CONFIG_PLATFORM_INFO "CC2538" - -/** - * @def OPENTHREAD_CONFIG_MAC_SOFTWARE_ACK_TIMEOUT_ENABLE - * - * Define to 1 if you want to enable software ACK timeout logic. - * - */ -#define OPENTHREAD_CONFIG_MAC_SOFTWARE_ACK_TIMEOUT_ENABLE 1 - -/** - * @def OPENTHREAD_CONFIG_MAC_SOFTWARE_RETRANSMIT_ENABLE - * - * Define to 1 if you want to enable software retransmission logic. - * - */ -#define OPENTHREAD_CONFIG_MAC_SOFTWARE_RETRANSMIT_ENABLE 1 - -/** - * @def OPENTHREAD_CONFIG_MAC_SOFTWARE_CSMA_BACKOFF_ENABLE - * - * Define to 1 if you want to enable software CSMA-CA backoff logic. - * - */ -#define OPENTHREAD_CONFIG_MAC_SOFTWARE_CSMA_BACKOFF_ENABLE 1 - -/** - * @def OPENTHREAD_CONFIG_MAC_SOFTWARE_TX_SECURITY_ENABLE - * - * Define to 1 if you want to enable software transmission security logic. - * - */ -#define OPENTHREAD_CONFIG_MAC_SOFTWARE_TX_SECURITY_ENABLE 0 - -/** - * @def OPENTHREAD_CONFIG_MAC_SOFTWARE_ENERGY_SCAN_ENABLE - * - * Define to 1 if you want to enable software energy scanning logic. - * - */ -#define OPENTHREAD_CONFIG_MAC_SOFTWARE_ENERGY_SCAN_ENABLE 1 - -/** - * @def OPENTHREAD_CONFIG_NCP_HDLC_ENABLE - * - * Define to 1 to enable NCP HDLC support. - * - */ -#define OPENTHREAD_CONFIG_NCP_HDLC_ENABLE 1 - -/** - * @def OPENTHREAD_CONFIG_CC2538_USE_RADIO_RX_INTERRUPT - * - * Enable support for using interrupt-driven radio reception. This allows - * for a single frame to be received whilst the CPU is busy processing some - * other code. - * - * To disable interrupts and just rely on polling, set this to 0. - * - */ -#ifndef OPENTHREAD_CONFIG_CC2538_USE_RADIO_RX_INTERRUPT -#define OPENTHREAD_CONFIG_CC2538_USE_RADIO_RX_INTERRUPT 1 -#endif - -/** - * @def OPENTHREAD_CONFIG_CC2538_WITH_CC2592 - * - * Enable support for the CC2592 range-extender front-end. - * - * This is a feature of the CC2538-CC2592 EM and other peripherals which - * extends the range of the bare CC2538 to over a kilometre line-of-sight. - * The CC2592 needs to be wired up to the RF port on the CC2538 in accordance - * with application note 130 ("Using CC2592 Front End With CC2538", TI doc - * SWRA447). - * - * If you have such a board, change this to 1. - * - */ -#ifndef OPENTHREAD_CONFIG_CC2538_WITH_CC2592 -#define OPENTHREAD_CONFIG_CC2538_WITH_CC2592 0 -#endif - -/** - * @def OPENTHREAD_CONFIG_CC2592_PA_EN_PIN - * - * Define the pin (on port C) that connects to the CC2592 PA_EN pin. - * - * One of the 3 observable channels on the CC2538 radio module will be - * configured to take the "PA power down" signal from the radio module itself, - * invert it, and emit it on this GPIO pin. Due to hardware constraints, it - * may only be connected to a pin on GPIO port C. - * - * The default (PC3) is as per TI recommendations in AN130. - * - */ -#ifndef OPENTHREAD_CONFIG_CC2592_PA_EN_PIN -#define OPENTHREAD_CONFIG_CC2592_PA_EN_PIN 3 -#endif - -/** - * @def OPENTHREAD_CONFIG_CC2592_LNA_EN_PIN - * - * Define the pin (on port C) that connects to the CC2592 LNA_EN pin. - * - * One of the 3 observable channels on the CC2538 radio module will be - * configured to take the "LNA power down" signal from the radio module itself, - * invert it, and emit it on this GPIO pin. Due to hardware constraints, it - * may only be connected to a pin on GPIO port C. - * - * The default (PC2) is as per TI recommendations in AN130. - * - */ -#ifndef OPENTHREAD_CONFIG_CC2592_LNA_EN_PIN -#define OPENTHREAD_CONFIG_CC2592_LNA_EN_PIN 2 -#endif - -/** - * @def OPENTHREAD_CONFIG_CC2592_USE_HGM - * - * Enable control of the high-gain mode signal. - * - * High-gain mode is enabled through the `HGM` pin on the CC2592, which may be - * connected to any free GPIO pin for software control, or may be linked to - * VDD or 0V to hard-wire it to a given state. - * - * Set this to 0 if you have wired this pin to a power rail, or have a - * non-standard way of controlling it. - * - */ -#ifndef OPENTHREAD_CONFIG_CC2592_USE_HGM -#define OPENTHREAD_CONFIG_CC2592_USE_HGM 1 -#endif - -/** - * @def OPENTHREAD_CONFIG_CC2538_RECEIVE_SENSITIVITY - * - * Set the CC2538 receive sensitivity. - * - * A bare CC2538 has a receive sensitivity of -88dBm. The CC2592 changes this - * to -85 or -81 depending on whether the HGM pin is high or low. If - * `OPENTHREAD_CONFIG_CC2592_USE_HGM` is 0, then this sets the receive - * sensitivity. - * - */ -#ifndef OPENTHREAD_CONFIG_CC2538_RECEIVE_SENSITIVITY -#define OPENTHREAD_CONFIG_CC2538_RECEIVE_SENSITIVITY -88 -#endif - -/** - * @def OPENTHREAD_CONFIG_CC2538_RSSI_OFFSET - * - * Set the CC2538 RSSI offset. This calibrates the RSSI readings received from - * the CC2538 radio module to give a reading in dBm. - * - * For a standard CC2538 (no front-end), the RSSI offset is 73. - * - * For a CC2592 hard-wired in high-gain mode, an offset of 85 should be used; - * or for low-gain mode, 81. If `OPENTHREAD_CONFIG_CC2592_USE_HGM` is 0, then - * this calibrates the RSSI value accordingly. - */ -#ifndef OPENTHREAD_CONFIG_CC2538_RSSI_OFFSET -#define OPENTHREAD_CONFIG_CC2538_RSSI_OFFSET 73 -#endif - -/** - * @def OPENTHREAD_CONFIG_CC2592_HGM_PORT - * - * Define the GPIO port that the HGM pin is connected to. It may be - * connected to any available GPIO pin. - * - * The default (GPIO port D) is as per TI recommendations. - * - */ -#ifndef OPENTHREAD_CONFIG_CC2592_HGM_PORT -#define OPENTHREAD_CONFIG_CC2592_HGM_PORT GPIO_D_BASE -#endif - -/** - * @def OPENTHREAD_CONFIG_CC2592_HGM_PIN - * - * Define the pin on the GPIO port that the HGM pin is connected to. It - * may be connected to any available GPIO pin. - * - * The default (PD2) is as per TI recommendations. - * - */ -#ifndef OPENTHREAD_CONFIG_CC2592_HGM_PIN -#define OPENTHREAD_CONFIG_CC2592_HGM_PIN 2 -#endif - -/** - * @def OPENTHREAD_CONFIG_CC2592_HGM_DEFAULT_STATE - * - * Define the default state of the CC2592's HGM pin. - * - * The default is to turn high-gain mode on. - * - */ -#ifndef OPENTHREAD_CONFIG_CC2592_HGM_DEFAULT_STATE -#define OPENTHREAD_CONFIG_CC2592_HGM_DEFAULT_STATE true -#endif - -/** - * @def OPENTHREAD_CONFIG_PLATFORM_FLASH_API_ENABLE - * - * Define to 1 to enable otPlatFlash* APIs to support non-volatile storage. - * - * When defined to 1, the platform MUST implement the otPlatFlash* APIs instead of the otPlatSettings* APIs. - * - */ -#define OPENTHREAD_CONFIG_PLATFORM_FLASH_API_ENABLE 1 - -#endif // OPENTHREAD_CORE_CC2538_CONFIG_H_ diff --git a/examples/platforms/cc2538/platform-cc2538.h b/examples/platforms/cc2538/platform-cc2538.h deleted file mode 100644 index f89b01bbb85..00000000000 --- a/examples/platforms/cc2538/platform-cc2538.h +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright (c) 2016, The OpenThread Authors. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the copyright holder nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -/** - * @file - * This file includes the platform-specific initializers. - * - */ - -#ifndef PLATFORM_CC2538_H_ -#define PLATFORM_CC2538_H_ - -#include -#include -#include -#include - -#include "cc2538-reg.h" - -// Global OpenThread instance structure -extern otInstance *sInstance; - -/** - * Initialize the debug uart - */ -void cc2538DebugUartInit(void); - -/** - * This function initializes the alarm service used by OpenThread. - * - */ -void cc2538AlarmInit(void); - -/** - * This function performs alarm driver processing. - * - * @param[in] aInstance The OpenThread instance structure. - * - */ -void cc2538AlarmProcess(otInstance *aInstance); - -/** - * This function initializes the radio service used by OpenThread. - * - */ -void cc2538RadioInit(void); - -/** - * This function performs radio driver processing. - * - * @param[in] aInstance The OpenThread instance structure. - * - */ -void cc2538RadioProcess(otInstance *aInstance); - -/** - * This function initializes the random number service used by OpenThread. - * - */ -void cc2538RandomInit(void); - -/** - * This function performs UART driver processing. - * - */ -void cc2538UartProcess(void); - -#if OPENTHREAD_CONFIG_CC2538_WITH_CC2592 && OPENTHREAD_CONFIG_CC2592_USE_HGM -/** - * Change the state of the CC2592 HGM pin. - * - * @param aState Whether or not to enable HGM - */ -void cc2538RadioSetHgm(bool aState); - -/** - * Retrieve the state of the CC2592 HGM pin. - */ -bool cc2538RadioGetHgm(void); -#endif // OPENTHREAD_CONFIG_CC2538_WITH_CC2592 && OPENTHREAD_CONFIG_CC2592_USE_HGM - -typedef enum -{ - OT_CC2538_TIMER_ENERGY_SCAN, ///< Internal timer for energy scan - OT_CC2538_TIMERS_COUNT, ///< Number of internal timers -} otCC2538Timer; - -/** - * This function sets the internal timer. - * - * @param[in] aTimer The timer identifier. - * @param[in] aDelay The delay to trigger the timer, and must be no more than `INT32_MAX`. - * - */ -void cc2538SetTimer(otCC2538Timer aTimer, uint32_t aDelay); - -#endif // PLATFORM_CC2538_H_ diff --git a/examples/platforms/cc2538/radio.c b/examples/platforms/cc2538/radio.c deleted file mode 100644 index 0878f115da0..00000000000 --- a/examples/platforms/cc2538/radio.c +++ /dev/null @@ -1,1273 +0,0 @@ -/* - * Copyright (c) 2016, The OpenThread Authors. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the copyright holder nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -/** - * @file - * This file implements the OpenThread platform abstraction for radio communication. - * - */ - -#include -#include -#include -#include -#include - -#include "platform-cc2538.h" -#include "utils/code_utils.h" - -#define RFCORE_XREG_RFIRQM0 0x4008868C // RF interrupt masks -#define RFCORE_XREG_RFIRQM1 0x40088690 // RF interrupt masks -#define RFCORE_XREG_RFERRM 0x40088694 // RF error interrupt mask - -#define RFCORE_SFR_RFIRQF0_RXMASKZERO 0x00000080 // RXENABLE is now completely clear -#define RFCORE_SFR_RFIRQF0_RXPKTDONE 0x00000040 // A complete frame has been received -#define RFCORE_SFR_RFIRQF0_FRAME_ACCEPTED 0x00000020 // Frame has passed frame filtering -#define RFCORE_SFR_RFIRQF0_SRC_MATCH_FOUND 0x00000010 // Source match is found -#define RFCORE_SFR_RFIRQF0_SRC_MATCH_DONE 0x00000008 // Source matching is complete -#define RFCORE_SFR_RFIRQF0_FIFOP 0x00000004 // The number of bytes in the RX fifo is above threshold -#define RFCORE_SFR_RFIRQF0_SFD 0x00000002 // SFD has been received or transmitted -#define RFCORE_SFR_RFIRQF0_ACT_UNUSED 0x00000001 // Reserved - -#define RFCORE_XREG_RFIRQM0_RXMASKZERO 0x00000080 -#define RFCORE_XREG_RFIRQM0_RXPKTDONE 0x00000040 -#define RFCORE_XREG_RFIRQM0_FRAME_ACCEPTED 0x00000020 -#define RFCORE_XREG_RFIRQM0_SRC_MATCH_FOUND 0x00000010 -#define RFCORE_XREG_RFIRQM0_SRC_MATCH_DONE 0x00000008 -#define RFCORE_XREG_RFIRQM0_FIFOP 0x00000004 -#define RFCORE_XREG_RFIRQM0_SFD 0x00000002 -#define RFCORE_XREG_RFIRQM0_ACT_UNUSED 0x00000001 - -#define RFCORE_SFR_RFIRQF1_CSP_WAIT 0x00000020 -#define RFCORE_SFR_RFIRQF1_CSP_STOP 0x00000010 -#define RFCORE_SFR_RFIRQF1_CSP_MANINT 0x00000008 -#define RFCORE_SFR_RFIRQF1_RF_IDLE 0x00000004 -#define RFCORE_SFR_RFIRQF1_TXDONE 0x00000002 -#define RFCORE_SFR_RFIRQF1_TXACKDONE 0x00000001 - -#define RFCORE_XREG_RFIRQM1_CSP_WAIT 0x00000020 -#define RFCORE_XREG_RFIRQM1_CSP_STOP 0x00000010 -#define RFCORE_XREG_RFIRQM1_CSP_MANINT 0x00000008 -#define RFCORE_XREG_RFIRQM1_RF_IDLE 0x00000004 -#define RFCORE_XREG_RFIRQM1_TXDONE 0x00000002 -#define RFCORE_XREG_RFIRQM1_TXACKDONE 0x00000001 - -#define RFCORE_XREG_RFERRM_STROBE_ERR 0x00000040 -#define RFCORE_XREG_RFERRM_TXUNDERF 0x00000020 -#define RFCORE_XREG_RFERRM_TXOVERF 0x00000010 -#define RFCORE_XREG_RFERRM_RXUNDERF 0x00000008 -#define RFCORE_XREG_RFERRM_RXOVERF 0x00000004 -#define RFCORE_XREG_RFERRM_RXABO 0x00000002 -#define RFCORE_XREG_RFERRM_NLOCK 0x00000001 - -enum -{ - IEEE802154_MIN_LENGTH = 5, - IEEE802154_MAX_LENGTH = 127, - IEEE802154_ACK_LENGTH = 5, - IEEE802154_FRAME_TYPE_MASK = 0x7, - IEEE802154_FRAME_TYPE_ACK = 0x2, - IEEE802154_FRAME_PENDING = 1 << 4, - IEEE802154_ACK_REQUEST = 1 << 5, - IEEE802154_DSN_OFFSET = 2, -}; - -enum -{ - CC2538_RSSI_OFFSET = OPENTHREAD_CONFIG_CC2538_RSSI_OFFSET, - // TI AN130 (SWRA447) Table 4 (bottom of page 3) - CC2592_RSSI_OFFSET_HGM = 85, - CC2592_RSSI_OFFSET_LGM = 81, - CC2538_CRC_BIT_MASK = 0x80, - CC2538_LQI_BIT_MASK = 0x7f, -}; - -// All values in dBm -enum -{ - CC2538_RECEIVE_SENSITIVITY = OPENTHREAD_CONFIG_CC2538_RECEIVE_SENSITIVITY, - // TI AN130 (SWRA447) Table 3 (middle of page 3) - CC2592_RECEIVE_SENSITIVITY_LGM = -99, - CC2592_RECEIVE_SENSITIVITY_HGM = -101, -}; - -typedef struct TxPowerTable -{ - int8_t mTxPowerVal; - uint8_t mTxPowerReg; -} TxPowerTable; - -// The transmit power table. -static const TxPowerTable sTxPowerTable[] = { -#if OPENTHREAD_CONFIG_CC2538_WITH_CC2592 - // CC2538 using CC2592 PA - // Values are from AN130 table 6 (page 4) - {22, 0xFF}, // 22.0dBm =~ 158.5mW - {21, 0xD5}, // 20.9dBm =~ 123.0mW - {20, 0xC5}, // 20.1dBm =~ 102.3mW - {19, 0xB0}, // 19.0dBm =~ 79.4mW - {18, 0xA1}, // 17.8dBm =~ 60.3mW - {16, 0x91}, // 16.4dBm =~ 43.7mW - {15, 0x88}, // 14.9dBm =~ 30.9mW - {13, 0x72}, // 13.0dBm =~ 20.0mW - {11, 0x62}, // 11.0dBm =~ 12.6mW - {10, 0x58}, // 9.5dBm =~ 8.9mW - {8, 0x42}, // 7.5dBm =~ 5.6mW -#else - // CC2538 operating "bare foot" - // Values are from SmartRF Studio 2.4.0 - {7, 0xFF}, // - {5, 0xED}, // - {3, 0xD5}, // - {1, 0xC5}, // - {0, 0xB6}, // - {-1, 0xB0}, // - {-3, 0xA1}, // - {-5, 0x91}, // - {-7, 0x88}, // - {-9, 0x72}, // - {-11, 0x62}, // - {-13, 0x58}, // - {-15, 0x42}, // - {-24, 0x00}, // -#endif -}; - -static otRadioFrame sTransmitFrame; -static otRadioFrame sReceiveFrame; -static otError sTransmitError; -static otError sReceiveError; - -static uint8_t sTransmitPsdu[IEEE802154_MAX_LENGTH]; -static uint8_t sReceivePsdu[IEEE802154_MAX_LENGTH]; -static uint8_t sChannel = 0; -static int8_t sTxPower = 0; - -static otRadioState sState = OT_RADIO_STATE_DISABLED; -static bool sIsReceiverEnabled = false; - -#if OPENTHREAD_CONFIG_LOG_PLATFORM && OPENTHREAD_CONFIG_CC2538_USE_RADIO_RX_INTERRUPT -// Debugging _and_ logging are enabled, so if there's a dropped frame -// we'll need to store the length here as using snprintf from an interrupt -// handler is not a good idea. -static uint8_t sDroppedFrameLength = 0; -#endif - -static int8_t cc2538RadioGetRssiOffset(void); - -void enableReceiver(void) -{ - if (!sIsReceiverEnabled) - { - otLogInfoPlat("Enabling receiver", NULL); - - // flush rxfifo - HWREG(RFCORE_SFR_RFST) = RFCORE_SFR_RFST_INSTR_FLUSHRX; - HWREG(RFCORE_SFR_RFST) = RFCORE_SFR_RFST_INSTR_FLUSHRX; - - // enable receiver - HWREG(RFCORE_SFR_RFST) = RFCORE_SFR_RFST_INSTR_RXON; - sIsReceiverEnabled = true; - } -} - -void disableReceiver(void) -{ - if (sIsReceiverEnabled) - { - otLogInfoPlat("Disabling receiver", NULL); - - while (HWREG(RFCORE_XREG_FSMSTAT1) & RFCORE_XREG_FSMSTAT1_TX_ACTIVE) - ; - - // flush rxfifo - HWREG(RFCORE_SFR_RFST) = RFCORE_SFR_RFST_INSTR_FLUSHRX; - HWREG(RFCORE_SFR_RFST) = RFCORE_SFR_RFST_INSTR_FLUSHRX; - - if (HWREG(RFCORE_XREG_RXENABLE) != 0) - { - // disable receiver - HWREG(RFCORE_SFR_RFST) = RFCORE_SFR_RFST_INSTR_RFOFF; - } - - sIsReceiverEnabled = false; - } -} - -void setChannel(uint8_t aChannel) -{ - if (sChannel != aChannel) - { - bool enabled = false; - - if (sIsReceiverEnabled) - { - disableReceiver(); - enabled = true; - } - - otLogInfoPlat("Channel=%d", aChannel); - - HWREG(RFCORE_XREG_FREQCTRL) = 11 + (aChannel - 11) * 5; - sChannel = aChannel; - - if (enabled) - { - enableReceiver(); - } - } -} - -void setTxPower(int8_t aTxPower) -{ - uint8_t i = 0; - - if (sTxPower != aTxPower) - { - otLogInfoPlat("TxPower=%d", aTxPower); - - for (i = sizeof(sTxPowerTable) / sizeof(TxPowerTable) - 1; i > 0; i--) - { - if (aTxPower < sTxPowerTable[i].mTxPowerVal) - { - break; - } - } - - HWREG(RFCORE_XREG_TXPOWER) = sTxPowerTable[i].mTxPowerReg; - sTxPower = aTxPower; - } -} - -static bool cc2538SrcMatchEnabled(void) -{ - return (HWREG(RFCORE_XREG_FRMCTRL1) & RFCORE_XREG_FRMCTRL1_PENDING_OR) == 0; -} - -static bool cc2538GetSrcMatchFoundIntFlag(void) -{ - bool flag = (HWREG(RFCORE_SFR_RFIRQF0) & RFCORE_SFR_RFIRQF0_SRC_MATCH_FOUND) != 0; - if (flag) - { - HWREG(RFCORE_SFR_RFIRQF0) &= ~RFCORE_SFR_RFIRQF0_SRC_MATCH_FOUND; - } - return flag; -} - -void otPlatRadioGetIeeeEui64(otInstance *aInstance, uint8_t *aIeeeEui64) -{ - OT_UNUSED_VARIABLE(aInstance); - - // EUI64 is in a mixed-endian format. Split in two halves, each 32-bit - // half is in little-endian format (machine endian). However, the - // most significant part of the EUI64 comes first, so we can't cheat - // with a uint64_t! - // - // See https://e2e.ti.com/support/wireless_connectivity/low_power_rf_tools/f/155/p/307344/1072252 - - volatile uint32_t *eui64 = &HWREG(IEEE_EUI64); - - // Read first 32-bits - uint32_t part = eui64[0]; - for (uint8_t i = 0; i < (OT_EXT_ADDRESS_SIZE / 2); i++) - { - aIeeeEui64[3 - i] = part; - part >>= 8; - } - - // Read the last 32-bits - part = eui64[1]; - for (uint8_t i = 0; i < (OT_EXT_ADDRESS_SIZE / 2); i++) - { - aIeeeEui64[7 - i] = part; - part >>= 8; - } -} - -void otPlatRadioSetPanId(otInstance *aInstance, uint16_t aPanid) -{ - OT_UNUSED_VARIABLE(aInstance); - - otLogInfoPlat("PANID=%X", aPanid); - - HWREG(RFCORE_FFSM_PAN_ID0) = aPanid & 0xFF; - HWREG(RFCORE_FFSM_PAN_ID1) = aPanid >> 8; -} - -void otPlatRadioSetExtendedAddress(otInstance *aInstance, const otExtAddress *aAddress) -{ - OT_UNUSED_VARIABLE(aInstance); - - otLogInfoPlat("ExtAddr=%X%X%X%X%X%X%X%X", aAddress->m8[7], aAddress->m8[6], aAddress->m8[5], aAddress->m8[4], - aAddress->m8[3], aAddress->m8[2], aAddress->m8[1], aAddress->m8[0]); - - for (int i = 0; i < 8; i++) - { - ((volatile uint32_t *)RFCORE_FFSM_EXT_ADDR0)[i] = aAddress->m8[i]; - } -} - -void otPlatRadioSetShortAddress(otInstance *aInstance, uint16_t aAddress) -{ - OT_UNUSED_VARIABLE(aInstance); - - otLogInfoPlat("ShortAddr=%X", aAddress); - - HWREG(RFCORE_FFSM_SHORT_ADDR0) = aAddress & 0xFF; - HWREG(RFCORE_FFSM_SHORT_ADDR1) = aAddress >> 8; -} - -void cc2538RadioInit(void) -{ - sTransmitFrame.mLength = 0; - sTransmitFrame.mPsdu = sTransmitPsdu; - sReceiveFrame.mLength = 0; - sReceiveFrame.mPsdu = sReceivePsdu; - -#if OPENTHREAD_CONFIG_CC2538_USE_RADIO_RX_INTERRUPT - // Enable interrupts for RX/TX, interrupt 26. - // That's NVIC index 0 (26 >> 5) bit 26 (26 & 0x1f). - HWREG(NVIC_EN0 + (0 * 4)) = (1 << 26); - HWREG(RFCORE_XREG_RFIRQM0) |= RFCORE_XREG_RFIRQM0_RXPKTDONE; -#endif - - // enable clock - HWREG(SYS_CTRL_RCGCRFC) = SYS_CTRL_RCGCRFC_RFC0; - HWREG(SYS_CTRL_SCGCRFC) = SYS_CTRL_SCGCRFC_RFC0; - HWREG(SYS_CTRL_DCGCRFC) = SYS_CTRL_DCGCRFC_RFC0; - - // Table 23-7. - HWREG(RFCORE_XREG_AGCCTRL1) = 0x15; - HWREG(RFCORE_XREG_TXFILTCFG) = 0x09; - HWREG(ANA_REGS_BASE + ANA_REGS_O_IVCTRL) = 0x0b; - - HWREG(RFCORE_XREG_CCACTRL0) = 0xf8; - HWREG(RFCORE_XREG_FIFOPCTRL) = IEEE802154_MAX_LENGTH; - - HWREG(RFCORE_XREG_FRMCTRL0) = RFCORE_XREG_FRMCTRL0_AUTOCRC | RFCORE_XREG_FRMCTRL0_AUTOACK; - - // default: SRCMATCH.SRC_MATCH_EN(1), SRCMATCH.AUTOPEND(1), - // SRCMATCH.PEND_DATAREQ_ONLY(1), RFCORE_XREG_FRMCTRL1_PENDING_OR(0) - - HWREG(RFCORE_XREG_TXPOWER) = sTxPowerTable[0].mTxPowerReg; - sTxPower = sTxPowerTable[0].mTxPowerVal; - -#if OPENTHREAD_CONFIG_CC2538_WITH_CC2592 - // PA_EN pin configuration. - // Step 1. make it an output - HWREG(GPIO_C_BASE | GPIO_O_DIR) |= GPIO_PIN(OPENTHREAD_CONFIG_CC2592_PA_EN_PIN); - // Step 2. Route PA_PD to OBS0 and invert it to produce PA_EN - HWREG_ARR(RFCORE_XREG_RFC_OBS_CTRL, 0) = RFCORE_XREG_RFC_OBS_POL_INV // Invert the output - | RFCORE_XREG_RFC_OBS_MUX_PA_PD; // PA "power down" signal - // Step 3. Connect the selected pin to OBS0 and enable OBS0. - HWREG_ARR(CCTEST_OBSSEL, OPENTHREAD_CONFIG_CC2592_PA_EN_PIN) = CCTEST_OBSSEL_EN // Enable the output - | CCTEST_OBSSEL_SEL_OBS0; // Select OBS0 - - // LNA_EN pin configuration. - HWREG(GPIO_C_BASE | GPIO_O_DIR) |= GPIO_PIN(OPENTHREAD_CONFIG_CC2592_LNA_EN_PIN); - HWREG_ARR(RFCORE_XREG_RFC_OBS_CTRL, 1) = RFCORE_XREG_RFC_OBS_POL_INV | RFCORE_XREG_RFC_OBS_MUX_LNA_PD; - HWREG_ARR(CCTEST_OBSSEL, OPENTHREAD_CONFIG_CC2592_LNA_EN_PIN) = CCTEST_OBSSEL_EN | CCTEST_OBSSEL_SEL_OBS1; - -#if OPENTHREAD_CONFIG_CC2592_USE_HGM - // HGM pin configuration. Set the pin state first so we don't glitch. - cc2538RadioSetHgm(OPENTHREAD_CONFIG_CC2592_HGM_DEFAULT_STATE); - HWREG(OPENTHREAD_CONFIG_CC2592_HGM_PORT | GPIO_O_DIR) |= GPIO_PIN(OPENTHREAD_CONFIG_CC2592_HGM_PIN); -#endif // OPENTHREAD_CONFIG_CC2592_USE_HGM -#endif // OPENTHREAD_CONFIG_CC2538_WITH_CC2592 - - otLogInfoPlat("Initialized", NULL); -} - -#if OPENTHREAD_CONFIG_CC2538_WITH_CC2592 && OPENTHREAD_CONFIG_CC2592_USE_HGM -void cc2538RadioSetHgm(bool aState) -{ - if (aState) - { - HWREG_ARR(OPENTHREAD_CONFIG_CC2592_HGM_PORT, GPIO_PIN(OPENTHREAD_CONFIG_CC2592_HGM_PIN)) = - GPIO_PIN(OPENTHREAD_CONFIG_CC2592_HGM_PIN); - } - else - { - HWREG_ARR(OPENTHREAD_CONFIG_CC2592_HGM_PORT, GPIO_PIN(OPENTHREAD_CONFIG_CC2592_HGM_PIN)) = 0; - } -} - -bool cc2538RadioGetHgm(void) -{ - if (HWREG_ARR(OPENTHREAD_CONFIG_CC2592_HGM_PORT, GPIO_PIN(OPENTHREAD_CONFIG_CC2592_HGM_PIN)) & - GPIO_PIN(OPENTHREAD_CONFIG_CC2592_HGM_PIN)) - { - return true; - } - else - { - return false; - } -} -#endif // OPENTHREAD_CONFIG_CC2538_WITH_CC2592 && OPENTHREAD_CONFIG_CC2592_USE_HGM - -bool otPlatRadioIsEnabled(otInstance *aInstance) -{ - OT_UNUSED_VARIABLE(aInstance); - - return (sState != OT_RADIO_STATE_DISABLED) ? true : false; -} - -otError otPlatRadioEnable(otInstance *aInstance) -{ - if (!otPlatRadioIsEnabled(aInstance)) - { - otLogDebgPlat("State=OT_RADIO_STATE_SLEEP", NULL); - sState = OT_RADIO_STATE_SLEEP; - } - - return OT_ERROR_NONE; -} - -otError otPlatRadioDisable(otInstance *aInstance) -{ - if (otPlatRadioIsEnabled(aInstance)) - { - otLogDebgPlat("State=OT_RADIO_STATE_DISABLED", NULL); - sState = OT_RADIO_STATE_DISABLED; - } - - return OT_ERROR_NONE; -} - -otError otPlatRadioSleep(otInstance *aInstance) -{ - OT_UNUSED_VARIABLE(aInstance); - - otError error = OT_ERROR_INVALID_STATE; - - if (sState == OT_RADIO_STATE_SLEEP || sState == OT_RADIO_STATE_RECEIVE) - { - otLogDebgPlat("State=OT_RADIO_STATE_SLEEP", NULL); - error = OT_ERROR_NONE; - sState = OT_RADIO_STATE_SLEEP; - disableReceiver(); - } - - return error; -} - -otError otPlatRadioReceive(otInstance *aInstance, uint8_t aChannel) -{ - OT_UNUSED_VARIABLE(aInstance); - - otError error = OT_ERROR_INVALID_STATE; - - if (sState != OT_RADIO_STATE_DISABLED) - { - otLogDebgPlat("State=OT_RADIO_STATE_RECEIVE", NULL); - - error = OT_ERROR_NONE; - sState = OT_RADIO_STATE_RECEIVE; - setChannel(aChannel); - sReceiveFrame.mChannel = aChannel; - enableReceiver(); - } - - return error; -} - -static void setupTransmit(otRadioFrame *aFrame) -{ - int i; - - // wait for current TX operation to complete, if any. - while (HWREG(RFCORE_XREG_FSMSTAT1) & RFCORE_XREG_FSMSTAT1_TX_ACTIVE) - ; - - // flush txfifo - HWREG(RFCORE_SFR_RFST) = RFCORE_SFR_RFST_INSTR_FLUSHTX; - HWREG(RFCORE_SFR_RFST) = RFCORE_SFR_RFST_INSTR_FLUSHTX; - - // frame length - HWREG(RFCORE_SFR_RFDATA) = aFrame->mLength; - - // frame data - for (i = 0; i < aFrame->mLength; i++) - { - HWREG(RFCORE_SFR_RFDATA) = aFrame->mPsdu[i]; - } - - setChannel(aFrame->mChannel); -} - -otError otPlatRadioTransmit(otInstance *aInstance, otRadioFrame *aFrame) -{ - OT_UNUSED_VARIABLE(aInstance); - - otError error = OT_ERROR_INVALID_STATE; - - if (sState == OT_RADIO_STATE_RECEIVE) - { - int i; - - error = OT_ERROR_NONE; - sState = OT_RADIO_STATE_TRANSMIT; - sTransmitError = OT_ERROR_NONE; - - setupTransmit(aFrame); - - // Set up a counter to inform us if we get stuck. - i = 1000000; - - // Wait for radio to enter receive state. - while ((HWREG(RFCORE_XREG_FSMSTAT1) & RFCORE_XREG_FSMSTAT1_RX_ACTIVE) == 0) - { - // Count down the cycles, and emit a message if we get to zero. - // Ideally, we should never get there! - if (i) - { - i--; - } - else - { - otLogCritPlat("Radio is stuck!!! FSMSTAT0=0x%08x FSMSTAT1=0x%08x RFERRF=0x%08x", - HWREG(RFCORE_XREG_FSMSTAT0), HWREG(RFCORE_XREG_FSMSTAT1), HWREG(RFCORE_SFR_RFERRF)); - i = 1000000; - } - - // Ensure we haven't overflowed the RX buffer in the mean time, as this - // will cause a deadlock here otherwise. Similarly, if we see an aborted - // RX, handle that here too to prevent deadlock. - if (HWREG(RFCORE_SFR_RFERRF) & (RFCORE_SFR_RFERRF_RXOVERF | RFCORE_SFR_RFERRF_RXABO)) - { - if (HWREG(RFCORE_SFR_RFERRF) & RFCORE_SFR_RFERRF_RXOVERF) - { - otLogCritPlat("RX Buffer Overflow detected", NULL); - } - - if (HWREG(RFCORE_SFR_RFERRF) & RFCORE_SFR_RFERRF_RXABO) - { - otLogCritPlat("Aborted RX detected", NULL); - } - - // Flush the RX buffer - HWREG(RFCORE_SFR_RFST) = RFCORE_SFR_RFST_INSTR_FLUSHRX; - HWREG(RFCORE_SFR_RFST) = RFCORE_SFR_RFST_INSTR_FLUSHRX; - } - - // Check for idle state. After flushing the RX buffer, we may wind up here. - if (!(HWREG(RFCORE_XREG_FSMSTAT1) & (RFCORE_XREG_FSMSTAT1_TX_ACTIVE | RFCORE_XREG_FSMSTAT1_RX_ACTIVE))) - { - otLogCritPlat("Idle state detected", NULL); - - // In this case, the state of our driver mis-matches our state. So force - // matters by clearing our channel variable and calling setChannel. This - // should bring our radio into the RX state, which should allow us to go - // into TX. - sChannel = 0; - setupTransmit(aFrame); - } - } - - // wait for valid rssi - while ((HWREG(RFCORE_XREG_RSSISTAT) & RFCORE_XREG_RSSISTAT_RSSI_VALID) == 0) - ; - - otEXPECT_ACTION(((HWREG(RFCORE_XREG_FSMSTAT1) & RFCORE_XREG_FSMSTAT1_CCA) && - !((HWREG(RFCORE_XREG_FSMSTAT1) & RFCORE_XREG_FSMSTAT1_SFD))), - sTransmitError = OT_ERROR_CHANNEL_ACCESS_FAILURE); - - // begin transmit - HWREG(RFCORE_SFR_RFST) = RFCORE_SFR_RFST_INSTR_TXON; - - otPlatRadioTxStarted(aInstance, aFrame); - - while (HWREG(RFCORE_XREG_FSMSTAT1) & RFCORE_XREG_FSMSTAT1_TX_ACTIVE) - ; - - otLogDebgPlat("Transmitted %d bytes", aFrame->mLength); - } - -exit: - return error; -} - -otRadioFrame *otPlatRadioGetTransmitBuffer(otInstance *aInstance) -{ - OT_UNUSED_VARIABLE(aInstance); - - return &sTransmitFrame; -} - -int8_t otPlatRadioGetRssi(otInstance *aInstance) -{ - OT_UNUSED_VARIABLE(aInstance); - - int8_t rssi = OT_RADIO_RSSI_INVALID; - - if ((HWREG(RFCORE_XREG_RSSISTAT) & RFCORE_XREG_RSSISTAT_RSSI_VALID) != 0) - { - rssi = HWREG(RFCORE_XREG_RSSI) & 0xff; - - if (rssi > cc2538RadioGetRssiOffset() - 128) - { - rssi -= cc2538RadioGetRssiOffset(); - } - else - { - rssi = -128; - } - } - - return rssi; -} - -otRadioCaps otPlatRadioGetCaps(otInstance *aInstance) -{ - OT_UNUSED_VARIABLE(aInstance); - - return OT_RADIO_CAPS_ENERGY_SCAN; -} - -static bool cc2538RadioGetPromiscuous(void) -{ - return (HWREG(RFCORE_XREG_FRMFILT0) & RFCORE_XREG_FRMFILT0_FRAME_FILTER_EN) == 0; -} - -bool otPlatRadioGetPromiscuous(otInstance *aInstance) -{ - OT_UNUSED_VARIABLE(aInstance); - - return cc2538RadioGetPromiscuous(); -} - -static int8_t cc2538RadioGetRssiOffset(void) -{ -#if OPENTHREAD_CONFIG_CC2538_WITH_CC2592 && OPENTHREAD_CONFIG_CC2592_USE_HGM - if (cc2538RadioGetHgm()) - { - return CC2592_RSSI_OFFSET_HGM; - } - else - { - return CC2592_RSSI_OFFSET_LGM; - } -#else // OPENTHREAD_CONFIG_CC2538_WITH_CC2592 && OPENTHREAD_CONFIG_CC2592_USE_HGM - return CC2538_RSSI_OFFSET; -#endif // OPENTHREAD_CONFIG_CC2538_WITH_CC2592 && OPENTHREAD_CONFIG_CC2592_USE_HGM -} - -void otPlatRadioSetPromiscuous(otInstance *aInstance, bool aEnable) -{ - OT_UNUSED_VARIABLE(aInstance); - - otLogInfoPlat("PromiscuousMode=%d", aEnable ? 1 : 0); - - if (aEnable) - { - HWREG(RFCORE_XREG_FRMFILT0) &= ~RFCORE_XREG_FRMFILT0_FRAME_FILTER_EN; - } - else - { - HWREG(RFCORE_XREG_FRMFILT0) |= RFCORE_XREG_FRMFILT0_FRAME_FILTER_EN; - } -} - -static void readFrame(void) -{ - uint8_t length; - uint8_t crcCorr; - int i; - - /* - * There is already a frame present in the buffer, return early so - * we do not overwrite it (hopefully we'll catch it on the next run). - */ - otEXPECT(sReceiveFrame.mLength == 0); - - otEXPECT(sState == OT_RADIO_STATE_RECEIVE || sState == OT_RADIO_STATE_TRANSMIT); - otEXPECT((HWREG(RFCORE_XREG_FSMSTAT1) & RFCORE_XREG_FSMSTAT1_FIFOP) != 0); - - // read length - length = HWREG(RFCORE_SFR_RFDATA); - otEXPECT(IEEE802154_MIN_LENGTH <= length && length <= IEEE802154_MAX_LENGTH); - -#if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE -#error Time sync requires the timestamp of SFD rather than that of rx done! -#else - // Timestamp - if (cc2538RadioGetPromiscuous()) -#endif - { - // The current driver only supports milliseconds resolution. - sReceiveFrame.mInfo.mRxInfo.mTimestamp = otPlatAlarmMilliGetNow() * 1000; - } - - // read psdu - for (i = 0; i < length - 2; i++) - { - sReceiveFrame.mPsdu[i] = HWREG(RFCORE_SFR_RFDATA); - } - - sReceiveFrame.mInfo.mRxInfo.mRssi = (int8_t)HWREG(RFCORE_SFR_RFDATA) - cc2538RadioGetRssiOffset(); - crcCorr = HWREG(RFCORE_SFR_RFDATA); - - if (crcCorr & CC2538_CRC_BIT_MASK) - { - sReceiveFrame.mLength = length; - sReceiveFrame.mInfo.mRxInfo.mLqi = crcCorr & CC2538_LQI_BIT_MASK; - - if (length > IEEE802154_ACK_LENGTH) - { - // Set ACK FP flag for the received frame according to whether SRC_MATCH_FOUND was triggered just before - // if SRC MATCH is not enabled, SRC_MATCH_FOUND is not triggered and all ACK FP is always set - sReceiveFrame.mInfo.mRxInfo.mAckedWithFramePending = - cc2538SrcMatchEnabled() ? cc2538GetSrcMatchFoundIntFlag() : true; - } - } - else - { - // resets rxfifo - HWREG(RFCORE_SFR_RFST) = RFCORE_SFR_RFST_INSTR_FLUSHRX; - HWREG(RFCORE_SFR_RFST) = RFCORE_SFR_RFST_INSTR_FLUSHRX; -#if OPENTHREAD_CONFIG_LOG_PLATFORM && OPENTHREAD_CONFIG_CC2538_USE_RADIO_RX_INTERRUPT - // Debugging _and_ logging are enabled, it may not be safe to do - // logging if we're in the interrupt context, so just stash the - // length and do the logging later. - sDroppedFrameLength = length; -#else - otLogDebgPlat("Dropping %d received bytes (Invalid CRC)", length); -#endif - } - - // check for rxfifo overflow - if ((HWREG(RFCORE_XREG_FSMSTAT1) & RFCORE_XREG_FSMSTAT1_FIFOP) != 0 && - (HWREG(RFCORE_XREG_FSMSTAT1) & RFCORE_XREG_FSMSTAT1_FIFO) == 0) - { - HWREG(RFCORE_SFR_RFST) = RFCORE_SFR_RFST_INSTR_FLUSHRX; - HWREG(RFCORE_SFR_RFST) = RFCORE_SFR_RFST_INSTR_FLUSHRX; - } - -exit: - return; -} - -void cc2538RadioProcess(otInstance *aInstance) -{ -#if OPENTHREAD_CONFIG_CC2538_USE_RADIO_RX_INTERRUPT - // Disable the receive interrupt so that sReceiveFrame doesn't get - // blatted by the interrupt handler while we're polling. - HWREG(RFCORE_XREG_RFIRQM0) &= ~RFCORE_XREG_RFIRQM0_RXPKTDONE; -#endif - - readFrame(); - -#if OPENTHREAD_CONFIG_LOG_PLATFORM && OPENTHREAD_CONFIG_CC2538_USE_RADIO_RX_INTERRUPT - if (sDroppedFrameLength != 0) - { - otLogDebgPlat("Dropping %d received bytes (Invalid CRC)", sDroppedFrameLength); - sDroppedFrameLength = 0; - } -#endif - - if ((sState == OT_RADIO_STATE_RECEIVE && sReceiveFrame.mLength > 0) || - (sState == OT_RADIO_STATE_TRANSMIT && sReceiveFrame.mLength > IEEE802154_ACK_LENGTH)) - { -#if OPENTHREAD_CONFIG_DIAG_ENABLE - - if (otPlatDiagModeGet()) - { - otPlatDiagRadioReceiveDone(aInstance, &sReceiveFrame, sReceiveError); - } - else -#endif - { - // signal MAC layer for each received frame if promiscuous is enabled - // otherwise only signal MAC layer for non-ACK frame - if (((HWREG(RFCORE_XREG_FRMFILT0) & RFCORE_XREG_FRMFILT0_FRAME_FILTER_EN) == 0) || - (sReceiveFrame.mLength > IEEE802154_ACK_LENGTH)) - { - otLogDebgPlat("Received %d bytes", sReceiveFrame.mLength); - otPlatRadioReceiveDone(aInstance, &sReceiveFrame, sReceiveError); - } - } - } - - if (sState == OT_RADIO_STATE_TRANSMIT) - { - if (sTransmitError != OT_ERROR_NONE || (sTransmitFrame.mPsdu[0] & IEEE802154_ACK_REQUEST) == 0) - { - if (sTransmitError != OT_ERROR_NONE) - { - otLogDebgPlat("Transmit failed ErrorCode=%d", sTransmitError); - } - - sState = OT_RADIO_STATE_RECEIVE; - -#if OPENTHREAD_CONFIG_DIAG_ENABLE - - if (otPlatDiagModeGet()) - { - otPlatDiagRadioTransmitDone(aInstance, &sTransmitFrame, sTransmitError); - } - else -#endif - { - otPlatRadioTxDone(aInstance, &sTransmitFrame, NULL, sTransmitError); - } - } - else if (sReceiveFrame.mLength == IEEE802154_ACK_LENGTH && - (sReceiveFrame.mPsdu[0] & IEEE802154_FRAME_TYPE_MASK) == IEEE802154_FRAME_TYPE_ACK && - (sReceiveFrame.mPsdu[IEEE802154_DSN_OFFSET] == sTransmitFrame.mPsdu[IEEE802154_DSN_OFFSET])) - { - sState = OT_RADIO_STATE_RECEIVE; - - otPlatRadioTxDone(aInstance, &sTransmitFrame, &sReceiveFrame, sTransmitError); - } - } - - sReceiveFrame.mLength = 0; - -#if OPENTHREAD_CONFIG_CC2538_USE_RADIO_RX_INTERRUPT - // Turn the receive interrupt handler back on now the buffer is clear. - HWREG(RFCORE_XREG_RFIRQM0) |= RFCORE_XREG_RFIRQM0_RXPKTDONE; -#endif -} - -void RFCoreRxTxIntHandler(void) -{ -#if OPENTHREAD_CONFIG_CC2538_USE_RADIO_RX_INTERRUPT - if (HWREG(RFCORE_SFR_RFIRQF0) & RFCORE_SFR_RFIRQF0_RXPKTDONE) - { - readFrame(); - - if (sReceiveFrame.mLength > 0) - { - // A frame has been received, disable the interrupt handler - // until the main loop has dealt with this previous frame, - // otherwise we might overwrite it whilst it is being read. - HWREG(RFCORE_XREG_RFIRQM0) &= ~RFCORE_XREG_RFIRQM0_RXPKTDONE; - } - } -#endif - - HWREG(RFCORE_SFR_RFIRQF0) = 0; -} - -void RFCoreErrIntHandler(void) -{ - HWREG(RFCORE_SFR_RFERRF) = 0; -} - -uint32_t getSrcMatchEntriesEnableStatus(bool aShort) -{ - uint32_t status = 0; - uint32_t *addr = aShort ? (uint32_t *)RFCORE_XREG_SRCSHORTEN0 : (uint32_t *)RFCORE_XREG_SRCEXTEN0; - - for (uint8_t i = 0; i < RFCORE_XREG_SRCMATCH_ENABLE_STATUS_SIZE; i++) - { - status |= HWREG(addr++) << (i * 8); - } - - return status; -} - -int8_t findSrcMatchShortEntry(uint16_t aShortAddress) -{ - int8_t entry = -1; - uint16_t shortAddr; - uint32_t bitMask; - uint32_t *addr = NULL; - uint32_t status = getSrcMatchEntriesEnableStatus(true); - - for (uint8_t i = 0; i < RFCORE_XREG_SRCMATCH_SHORT_ENTRIES; i++) - { - bitMask = 0x00000001 << i; - - if ((status & bitMask) == 0) - { - continue; - } - - addr = (uint32_t *)RFCORE_FFSM_SRCADDRESS_TABLE + (i * RFCORE_XREG_SRCMATCH_SHORT_ENTRY_OFFSET); - - shortAddr = HWREG(addr + 2); - shortAddr |= HWREG(addr + 3) << 8; - - if ((shortAddr == aShortAddress)) - { - entry = i; - break; - } - } - - return entry; -} - -int8_t findSrcMatchExtEntry(const otExtAddress *aExtAddress) -{ - int8_t entry = -1; - uint32_t bitMask; - uint32_t *addr = NULL; - uint32_t status = getSrcMatchEntriesEnableStatus(false); - - for (uint8_t i = 0; i < RFCORE_XREG_SRCMATCH_EXT_ENTRIES; i++) - { - uint8_t j = 0; - bitMask = 0x00000001 << 2 * i; - - if ((status & bitMask) == 0) - { - continue; - } - - addr = (uint32_t *)RFCORE_FFSM_SRCADDRESS_TABLE + (i * RFCORE_XREG_SRCMATCH_EXT_ENTRY_OFFSET); - - for (j = 0; j < sizeof(otExtAddress); j++) - { - if (HWREG(addr + j) != aExtAddress->m8[j]) - { - break; - } - } - - if (j == sizeof(otExtAddress)) - { - entry = i; - break; - } - } - - return entry; -} - -void setSrcMatchEntryEnableStatus(bool aShort, uint8_t aEntry, bool aEnable) -{ - uint8_t entry = aShort ? aEntry : (2 * aEntry); - uint8_t index = entry / 8; - uint32_t *addrEn = aShort ? (uint32_t *)RFCORE_XREG_SRCSHORTEN0 : (uint32_t *)RFCORE_XREG_SRCEXTEN0; - uint32_t *addrAutoPendEn = aShort ? (uint32_t *)RFCORE_FFSM_SRCSHORTPENDEN0 : (uint32_t *)RFCORE_FFSM_SRCEXTPENDEN0; - uint32_t bitMask = 0x00000001; - - if (aEnable) - { - HWREG(addrEn + index) |= (bitMask) << (entry % 8); - HWREG(addrAutoPendEn + index) |= (bitMask) << (entry % 8); - } - else - { - HWREG(addrEn + index) &= ~((bitMask) << (entry % 8)); - HWREG(addrAutoPendEn + index) &= ~((bitMask) << (entry % 8)); - } -} - -int8_t findSrcMatchAvailEntry(bool aShort) -{ - int8_t entry = -1; - uint32_t bitMask; - uint32_t shortEnableStatus = getSrcMatchEntriesEnableStatus(true); - uint32_t extEnableStatus = getSrcMatchEntriesEnableStatus(false); - - otLogDebgPlat("Short enable status: 0x%x", shortEnableStatus); - otLogDebgPlat("Ext enable status: 0x%x", extEnableStatus); - - if (aShort) - { - bitMask = 0x00000001; - - for (uint8_t i = 0; i < RFCORE_XREG_SRCMATCH_SHORT_ENTRIES; i++) - { - if ((extEnableStatus & bitMask) == 0) - { - if ((shortEnableStatus & bitMask) == 0) - { - entry = i; - break; - } - } - - if (i % 2 == 1) - { - extEnableStatus = extEnableStatus >> 2; - } - - shortEnableStatus = shortEnableStatus >> 1; - } - } - else - { - bitMask = 0x00000003; - - for (uint8_t i = 0; i < RFCORE_XREG_SRCMATCH_EXT_ENTRIES; i++) - { - if (((extEnableStatus | shortEnableStatus) & bitMask) == 0) - { - entry = i; - break; - } - - extEnableStatus = extEnableStatus >> 2; - shortEnableStatus = shortEnableStatus >> 2; - } - } - - return entry; -} - -void cc2538EnergyScanTimerHandler(void) -{ - int8_t rssi = otPlatRadioGetRssi(sInstance); - - disableReceiver(); - - HWREG(RFCORE_XREG_FRMCTRL0) &= ~RFCORE_XREG_FRMCTRL0_ENERGY_SCAN; - HWREG(RFCORE_XREG_FREQCTRL) = 11 + (sChannel - 11) * 5; - - if (sIsReceiverEnabled) - { - enableReceiver(); - } - - otPlatRadioEnergyScanDone(sInstance, rssi); -} - -void otPlatRadioEnableSrcMatch(otInstance *aInstance, bool aEnable) -{ - OT_UNUSED_VARIABLE(aInstance); - - otLogInfoPlat("EnableSrcMatch=%d", aEnable ? 1 : 0); - - if (aEnable) - { - // only set FramePending when ack for data poll if there are queued messages - // for entries in the source match table. - HWREG(RFCORE_XREG_FRMCTRL1) &= ~RFCORE_XREG_FRMCTRL1_PENDING_OR; - } - else - { - // set FramePending for all ack. - HWREG(RFCORE_XREG_FRMCTRL1) |= RFCORE_XREG_FRMCTRL1_PENDING_OR; - } -} - -otError otPlatRadioAddSrcMatchShortEntry(otInstance *aInstance, uint16_t aShortAddress) -{ - OT_UNUSED_VARIABLE(aInstance); - - otError error = OT_ERROR_NONE; - int8_t entry = findSrcMatchAvailEntry(true); - uint32_t *addr = (uint32_t *)RFCORE_FFSM_SRCADDRESS_TABLE; - - otLogDebgPlat("Add ShortAddr entry: %d", entry); - - otEXPECT_ACTION(entry >= 0, error = OT_ERROR_NO_BUFS); - - addr += (entry * RFCORE_XREG_SRCMATCH_SHORT_ENTRY_OFFSET); - - HWREG(addr++) = HWREG(RFCORE_FFSM_PAN_ID0); - HWREG(addr++) = HWREG(RFCORE_FFSM_PAN_ID1); - HWREG(addr++) = aShortAddress & 0xFF; - HWREG(addr++) = aShortAddress >> 8; - - setSrcMatchEntryEnableStatus(true, (uint8_t)(entry), true); - -exit: - return error; -} - -otError otPlatRadioAddSrcMatchExtEntry(otInstance *aInstance, const otExtAddress *aExtAddress) -{ - OT_UNUSED_VARIABLE(aInstance); - - otError error = OT_ERROR_NONE; - int8_t entry = findSrcMatchAvailEntry(false); - uint32_t *addr = (uint32_t *)RFCORE_FFSM_SRCADDRESS_TABLE; - - otLogDebgPlat("Add ExtAddr entry: %d", entry); - - otEXPECT_ACTION(entry >= 0, error = OT_ERROR_NO_BUFS); - - addr += (entry * RFCORE_XREG_SRCMATCH_EXT_ENTRY_OFFSET); - - for (uint8_t i = 0; i < sizeof(otExtAddress); i++) - { - HWREG(addr++) = aExtAddress->m8[i]; - } - - setSrcMatchEntryEnableStatus(false, (uint8_t)(entry), true); - -exit: - return error; -} - -otError otPlatRadioClearSrcMatchShortEntry(otInstance *aInstance, uint16_t aShortAddress) -{ - OT_UNUSED_VARIABLE(aInstance); - - otError error = OT_ERROR_NONE; - int8_t entry = findSrcMatchShortEntry(aShortAddress); - - otLogDebgPlat("Clear ShortAddr entry: %d", entry); - - otEXPECT_ACTION(entry >= 0, error = OT_ERROR_NO_ADDRESS); - - setSrcMatchEntryEnableStatus(true, (uint8_t)(entry), false); - -exit: - return error; -} - -otError otPlatRadioClearSrcMatchExtEntry(otInstance *aInstance, const otExtAddress *aExtAddress) -{ - OT_UNUSED_VARIABLE(aInstance); - - otError error = OT_ERROR_NONE; - int8_t entry = findSrcMatchExtEntry(aExtAddress); - - otLogDebgPlat("Clear ExtAddr entry: %d", entry); - - otEXPECT_ACTION(entry >= 0, error = OT_ERROR_NO_ADDRESS); - - setSrcMatchEntryEnableStatus(false, (uint8_t)(entry), false); - -exit: - return error; -} - -void otPlatRadioClearSrcMatchShortEntries(otInstance *aInstance) -{ - OT_UNUSED_VARIABLE(aInstance); - - uint32_t *addrEn = (uint32_t *)RFCORE_XREG_SRCSHORTEN0; - uint32_t *addrAutoPendEn = (uint32_t *)RFCORE_FFSM_SRCSHORTPENDEN0; - - otLogDebgPlat("Clear ShortAddr entries", NULL); - - for (uint8_t i = 0; i < RFCORE_XREG_SRCMATCH_ENABLE_STATUS_SIZE; i++) - { - HWREG(addrEn++) = 0; - HWREG(addrAutoPendEn++) = 0; - } -} - -void otPlatRadioClearSrcMatchExtEntries(otInstance *aInstance) -{ - OT_UNUSED_VARIABLE(aInstance); - - uint32_t *addrEn = (uint32_t *)RFCORE_XREG_SRCEXTEN0; - uint32_t *addrAutoPendEn = (uint32_t *)RFCORE_FFSM_SRCEXTPENDEN0; - - otLogDebgPlat("Clear ExtAddr entries", NULL); - - for (uint8_t i = 0; i < RFCORE_XREG_SRCMATCH_ENABLE_STATUS_SIZE; i++) - { - HWREG(addrEn++) = 0; - HWREG(addrAutoPendEn++) = 0; - } -} - -otError otPlatRadioEnergyScan(otInstance *aInstance, uint8_t aScanChannel, uint16_t aScanDuration) -{ - OT_UNUSED_VARIABLE(aInstance); - - otLogInfoPlat("ScanChannel=%d", aScanChannel); - - if (aScanChannel != sChannel) - { - if (sIsReceiverEnabled) - { - disableReceiver(); - } - - HWREG(RFCORE_XREG_FREQCTRL) = 11 + (aScanChannel - 11) * 5; - - enableReceiver(); - } - else if (!sIsReceiverEnabled) - { - enableReceiver(); - } - - // Collect peak signal strength - HWREG(RFCORE_XREG_FRMCTRL0) |= RFCORE_XREG_FRMCTRL0_ENERGY_SCAN; - - cc2538SetTimer(OT_CC2538_TIMER_ENERGY_SCAN, aScanDuration); - - return OT_ERROR_NONE; -} - -otError otPlatRadioGetTransmitPower(otInstance *aInstance, int8_t *aPower) -{ - OT_UNUSED_VARIABLE(aInstance); - - otError error = OT_ERROR_NONE; - - otEXPECT_ACTION(aPower != NULL, error = OT_ERROR_INVALID_ARGS); - *aPower = sTxPower; - -exit: - return error; -} - -otError otPlatRadioSetTransmitPower(otInstance *aInstance, int8_t aPower) -{ - OT_UNUSED_VARIABLE(aInstance); - - setTxPower(aPower); - return OT_ERROR_NONE; -} - -otError otPlatRadioGetCcaEnergyDetectThreshold(otInstance *aInstance, int8_t *aThreshold) -{ - OT_UNUSED_VARIABLE(aInstance); - OT_UNUSED_VARIABLE(aThreshold); - - return OT_ERROR_NOT_IMPLEMENTED; -} - -otError otPlatRadioSetCcaEnergyDetectThreshold(otInstance *aInstance, int8_t aThreshold) -{ - OT_UNUSED_VARIABLE(aInstance); - OT_UNUSED_VARIABLE(aThreshold); - - return OT_ERROR_NOT_IMPLEMENTED; -} - -int8_t otPlatRadioGetReceiveSensitivity(otInstance *aInstance) -{ - OT_UNUSED_VARIABLE(aInstance); - -#if OPENTHREAD_CONFIG_CC2538_WITH_CC2592 && OPENTHREAD_CONFIG_CC2592_USE_HGM - if (cc2538RadioGetHgm()) - { - return CC2592_RECEIVE_SENSITIVITY_HGM; - } - else - { - return CC2592_RECEIVE_SENSITIVITY_LGM; - } -#else // OPENTHREAD_CONFIG_CC2538_WITH_CC2592 && OPENTHREAD_CONFIG_CC2592_USE_HGM - return CC2538_RECEIVE_SENSITIVITY; -#endif // OPENTHREAD_CONFIG_CC2538_WITH_CC2592 && OPENTHREAD_CONFIG_CC2592_USE_HGM -} diff --git a/examples/platforms/cc2538/rom-utility.h b/examples/platforms/cc2538/rom-utility.h deleted file mode 100644 index d0686efdee7..00000000000 --- a/examples/platforms/cc2538/rom-utility.h +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (c) 2016, The OpenThread Authors. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the copyright holder nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef ROM_UTILITY_H_ -#define ROM_UTILITY_H_ - -#define ROM_API_TABLE_ADDR 0x00000048 - -typedef uint32_t (*volatile FPTR_CRC32_T)(uint8_t * /*pData*/, uint32_t /*byteCount*/); -typedef uint32_t (*volatile FPTR_GETFLSIZE_T)(void); -typedef uint32_t (*volatile FPTR_GETCHIPID_T)(void); -typedef int32_t (*volatile FPTR_PAGEERASE_T)(uint32_t /*FlashAddr*/, uint32_t /*Size*/); -typedef int32_t (*volatile FPTR_PROGFLASH_T)(uint32_t * /*pRamData*/, uint32_t /*FlashAdr*/, uint32_t /*ByteCount*/); -typedef void (*volatile FPTR_RESETDEV_T)(void); -typedef void *(*volatile FPTR_MEMSET_T)(void * /*s*/, int32_t /*c*/, uint32_t /*n*/); -typedef void *(*volatile FPTR_MEMCPY_T)(void * /*s1*/, const void * /*s2*/, uint32_t /*n*/); -typedef int32_t (*volatile FPTR_MEMCMP_T)(const void * /*s1*/, const void * /*s2*/, uint32_t /*n*/); -typedef void *(*volatile FPTR_MEMMOVE_T)(void * /*s1*/, const void * /*s2*/, uint32_t /*n*/); - -typedef struct -{ - FPTR_CRC32_T Crc32; - FPTR_GETFLSIZE_T GetFlashSize; - FPTR_GETCHIPID_T GetChipId; - FPTR_PAGEERASE_T PageErase; - FPTR_PROGFLASH_T ProgramFlash; - FPTR_RESETDEV_T ResetDevice; - FPTR_MEMSET_T memset; - FPTR_MEMCPY_T memcpy; - FPTR_MEMCMP_T memcmp; - FPTR_MEMMOVE_T memmove; -} ROM_API_T; - -// clang-format off - -#define P_ROM_API ((ROM_API_T*)ROM_API_TABLE_ADDR) - -#define ROM_Crc32(a,b) P_ROM_API->Crc32(a,b) -#define ROM_GetFlashSize() P_ROM_API->GetFlashSize() -#define ROM_GetChipId() P_ROM_API->GetChipId() -#define ROM_PageErase(a,b) P_ROM_API->PageErase(a,b) -#define ROM_ProgramFlash(a,b,c) P_ROM_API->ProgramFlash(a,b,c) -#define ROM_ResetDevice() P_ROM_API->ResetDevice() -#define ROM_Memset(a,b,c) P_ROM_API->memset(a,b,c) -#define ROM_Memcpy(a,b,c) P_ROM_API->memcpy(a,b,c) -#define ROM_Memcmp(a,b,c) P_ROM_API->memcmp(a,b,c) -#define ROM_Memmove(a,b,c) P_ROM_API->memmove(a,b,c) - -// clang-format on - -#endif // ROM_UTILITY_H_ diff --git a/examples/platforms/cc2538/startup-gcc.c b/examples/platforms/cc2538/startup-gcc.c deleted file mode 100644 index f174bfa099b..00000000000 --- a/examples/platforms/cc2538/startup-gcc.c +++ /dev/null @@ -1,215 +0,0 @@ -/* - * Copyright (c) 2016, The OpenThread Authors. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the copyright holder nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -/** - * @file - * This file implements gcc-specific startup code for the cc2538. - */ - -#include -#include - -#include "cc2538-reg.h" - -extern uint8_t _ldata; -extern uint8_t _data; -extern uint8_t _edata; -extern uint8_t _bss; -extern uint8_t _ebss; -extern uint8_t _init_array; -extern uint8_t _einit_array; - -__extension__ typedef int __guard __attribute__((mode(__DI__))); - -int __cxa_guard_acquire(__guard *g) -{ - return !*(char *)(g); -} - -void __cxa_guard_release(__guard *g) -{ - *(char *)g = 1; -} - -void __cxa_guard_abort(__guard *g) -{ - (void)g; -} - -void __cxa_pure_virtual(void) -{ - while (1) - ; -} - -void IntDefaultHandler(void); -void ResetHandler(void); - -extern void SysTick_Handler(void); -extern void UART0IntHandler(void); -extern void RFCoreRxTxIntHandler(void); -extern void RFCoreErrIntHandler(void); -extern void main(void); - -static uint64_t stack[640] __attribute__((section(".stack"))); - -__attribute__((section(".vectors"), used)) void (*const vectors[])(void) = { - (void (*)(void))((unsigned long)stack + sizeof(stack)), // Initial Stack Pointer - ResetHandler, // 1 The reset handler - ResetHandler, // 2 The NMI handler - IntDefaultHandler, // 3 The hard fault handler - IntDefaultHandler, // 4 The MPU fault handler - IntDefaultHandler, // 5 The bus fault handler - IntDefaultHandler, // 6 The usage fault handler - 0, // 7 Reserved - 0, // 8 Reserved - 0, // 9 Reserved - 0, // 10 Reserved - IntDefaultHandler, // 11 SVCall handler - IntDefaultHandler, // 12 Debug monitor handler - 0, // 13 Reserved - IntDefaultHandler, // 14 The PendSV handler - SysTick_Handler, // 15 The SysTick handler - IntDefaultHandler, // 16 GPIO Port A - IntDefaultHandler, // 17 GPIO Port B - IntDefaultHandler, // 18 GPIO Port C - IntDefaultHandler, // 19 GPIO Port D - 0, // 20 none - UART0IntHandler, // 21 UART0 Rx and Tx - IntDefaultHandler, // 22 UART1 Rx and Tx - IntDefaultHandler, // 23 SSI0 Rx and Tx - IntDefaultHandler, // 24 I2C Master and Slave - 0, // 25 Reserved - 0, // 26 Reserved - 0, // 27 Reserved - 0, // 28 Reserved - 0, // 29 Reserved - IntDefaultHandler, // 30 ADC Sequence 0 - 0, // 31 Reserved - 0, // 32 Reserved - 0, // 33 Reserved - IntDefaultHandler, // 34 Watchdog timer, timer 0 - IntDefaultHandler, // 35 Timer 0 subtimer A - IntDefaultHandler, // 36 Timer 0 subtimer B - IntDefaultHandler, // 37 Timer 1 subtimer A - IntDefaultHandler, // 38 Timer 1 subtimer B - IntDefaultHandler, // 39 Timer 2 subtimer A - IntDefaultHandler, // 40 Timer 2 subtimer B - IntDefaultHandler, // 41 Analog Comparator 0 - RFCoreRxTxIntHandler, // 42 RFCore Rx/Tx - RFCoreErrIntHandler, // 43 RFCore Error - IntDefaultHandler, // 44 IcePick - IntDefaultHandler, // 45 FLASH Control - IntDefaultHandler, // 46 AES - IntDefaultHandler, // 47 PKA - IntDefaultHandler, // 48 Sleep Timer - IntDefaultHandler, // 49 MacTimer - IntDefaultHandler, // 50 SSI1 Rx and Tx - IntDefaultHandler, // 51 Timer 3 subtimer A - IntDefaultHandler, // 52 Timer 3 subtimer B - 0, // 53 Reserved - 0, // 54 Reserved - 0, // 55 Reserved - 0, // 56 Reserved - 0, // 57 Reserved - 0, // 58 Reserved - 0, // 59 Reserved - IntDefaultHandler, // 60 USB 2538 - 0, // 61 Reserved - IntDefaultHandler, // 62 uDMA - IntDefaultHandler, // 63 uDMA Error -}; - -void IntDefaultHandler(void) -{ - while (1) - ; -} - -// clang-format off - -#define FLASH_CCA_BOOTLDR_CFG_DISABLE 0xEFFFFFFF ///< Disable backdoor function -#define FLASH_CCA_BOOTLDR_CFG_ENABLE 0xF0FFFFFF ///< Enable backdoor function -#define FLASH_CCA_BOOTLDR_CFG_ACTIVE_HIGH 0x08000000 ///< Selected pin on pad A active high -#define FLASH_CCA_BOOTLDR_CFG_PORT_A_PIN_M 0x07000000 ///< Selected pin on pad A mask -#define FLASH_CCA_BOOTLDR_CFG_PORT_A_PIN_S 24 ///< Selected pin on pad A shift -#define FLASH_CCA_IMAGE_VALID 0x00000000 ///< Indicates valid image in flash - -#define FLASH_CCA_CONF_BOOTLDR_BACKDOOR_PORT_A_PIN 3 ///< Select Button on SmartRF06 Eval Board - -// clang-format on - -typedef struct -{ - uint32_t ui32BootldrCfg; - uint32_t ui32ImageValid; - uint32_t ui32ImageVectorAddr; - uint8_t ui8lock[32]; -} flash_cca_lock_page_t; - -__attribute__((__section__(".flashcca"), used)) const flash_cca_lock_page_t flash_cca_lock_page = { - FLASH_CCA_BOOTLDR_CFG_ENABLE | (FLASH_CCA_CONF_BOOTLDR_BACKDOOR_PORT_A_PIN << FLASH_CCA_BOOTLDR_CFG_PORT_A_PIN_S), - FLASH_CCA_IMAGE_VALID, - (uint32_t)&vectors, - {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}}; - -typedef void (*init_fn_t)(void); - -void ResetHandler(void) -{ - HWREG(SYS_CTRL_EMUOVR) = 0xFF; - - // configure clocks - HWREG(SYS_CTRL_CLOCK_CTRL) |= SYS_CTRL_CLOCK_CTRL_AMP_DET; - HWREG(SYS_CTRL_CLOCK_CTRL) = SYS_CTRL_SYSDIV_32MHZ; - - // alternate map - HWREG(SYS_CTRL_I_MAP) |= SYS_CTRL_I_MAP_ALTMAP; - - // copy the data segment initializers from flash to SRAM - memcpy(&_data, &_ldata, &_edata - &_data); - - // zero-fill the bss segment - memset(&_bss, 0, &_ebss - &_bss); - - // C++ runtime initialization (BSS, Data, relocation, etc.) - init_fn_t *fp; - - for (fp = (init_fn_t *)&_init_array; fp < (init_fn_t *)&_einit_array; fp++) - { - (*fp)(); - } - - // call the application's entry point - main(); - - // end here if main() returns - while (1) - ; -} diff --git a/examples/platforms/cc2538/system.c b/examples/platforms/cc2538/system.c deleted file mode 100644 index 0d1cd63de79..00000000000 --- a/examples/platforms/cc2538/system.c +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (c) 2016, The OpenThread Authors. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the copyright holder nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -/** - * @file - * @brief - * This file includes the platform-specific initializers. - */ -#include "platform-cc2538.h" -#include - -otInstance *sInstance; - -void otSysInit(int argc, char *argv[]) -{ - OT_UNUSED_VARIABLE(argc); - OT_UNUSED_VARIABLE(argv); - -#if OPENTHREAD_CONFIG_ENABLE_DEBUG_UART - cc2538DebugUartInit(); -#endif - cc2538AlarmInit(); - cc2538RandomInit(); - cc2538RadioInit(); -} - -bool otSysPseudoResetWasRequested(void) -{ - return false; -} - -void otSysProcessDrivers(otInstance *aInstance) -{ - sInstance = aInstance; - - // should sleep and wait for interrupts here - - cc2538UartProcess(); - cc2538RadioProcess(aInstance); - cc2538AlarmProcess(aInstance); -} diff --git a/examples/platforms/cc2538/uart.c b/examples/platforms/cc2538/uart.c deleted file mode 100644 index 3325c990427..00000000000 --- a/examples/platforms/cc2538/uart.c +++ /dev/null @@ -1,318 +0,0 @@ -/* - * Copyright (c) 2016, The OpenThread Authors. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the copyright holder nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -/** - * @file - * This file implements the OpenThread platform abstraction for UART communication. - * - */ - -#include -#include - -#include -#include -#include - -#include -#include - -#include "platform-cc2538.h" -#include "utils/code_utils.h" -#include "utils/uart.h" - -enum -{ - kPlatformClock = 32000000, - kBaudRate = 115200, - kReceiveBufferSize = 128, -}; - -extern void UART0IntHandler(void); - -static void processReceive(void); -static void processTransmit(void); - -static const uint8_t *sTransmitBuffer = NULL; -static uint16_t sTransmitLength = 0; - -typedef struct RecvBuffer -{ - // The data buffer - uint8_t mBuffer[kReceiveBufferSize]; - // The offset of the first item written to the list. - uint16_t mHead; - // The offset of the next item to be written to the list. - uint16_t mTail; -} RecvBuffer; - -static RecvBuffer sReceive; - -static void enable_uart_clocks(void) -{ - static int uart_clocks_done = 0; - - if (uart_clocks_done) - { - return; - } - - uart_clocks_done = 1; - -#if OPENTHREAD_CONFIG_ENABLE_DEBUG_UART - HWREG(SYS_CTRL_RCGCUART) = (SYS_CTRL_RCGCUART_UART0 | SYS_CTRL_RCGCUART_UART1); - HWREG(SYS_CTRL_SCGCUART) = (SYS_CTRL_SCGCUART_UART0 | SYS_CTRL_SCGCUART_UART1); - HWREG(SYS_CTRL_DCGCUART) = (SYS_CTRL_DCGCUART_UART0 | SYS_CTRL_DCGCUART_UART1); -#else - HWREG(SYS_CTRL_RCGCUART) = SYS_CTRL_RCGCUART_UART0; - HWREG(SYS_CTRL_SCGCUART) = SYS_CTRL_SCGCUART_UART0; - HWREG(SYS_CTRL_DCGCUART) = SYS_CTRL_DCGCUART_UART0; -#endif -} - -otError otPlatUartEnable(void) -{ - uint32_t div; - - sReceive.mHead = 0; - sReceive.mTail = 0; - - // clock - enable_uart_clocks(); - - HWREG(UART0_BASE + UART_O_CC) = 0; - - // tx pin - HWREG(IOC_PA1_SEL) = IOC_MUX_OUT_SEL_UART0_TXD; - HWREG(IOC_PA1_OVER) = IOC_OVERRIDE_OE; - HWREG(GPIO_A_BASE + GPIO_O_AFSEL) |= GPIO_PIN_1; - - // rx pin - HWREG(IOC_UARTRXD_UART0) = IOC_PAD_IN_SEL_PA0; - HWREG(IOC_PA0_OVER) = IOC_OVERRIDE_DIS; - HWREG(GPIO_A_BASE + GPIO_O_AFSEL) |= GPIO_PIN_0; - - HWREG(UART0_BASE + UART_O_CTL) = 0; - - // baud rate - div = (((kPlatformClock * 8) / kBaudRate) + 1) / 2; - HWREG(UART0_BASE + UART_O_IBRD) = div / 64; - HWREG(UART0_BASE + UART_O_FBRD) = div % 64; - HWREG(UART0_BASE + UART_O_LCRH) = UART_CONFIG_WLEN_8 | UART_CONFIG_STOP_ONE | UART_CONFIG_PAR_NONE; - - // configure interrupts - HWREG(UART0_BASE + UART_O_IM) |= UART_IM_RXIM | UART_IM_RTIM; - - // enable - HWREG(UART0_BASE + UART_O_CTL) = UART_CTL_UARTEN | UART_CTL_TXE | UART_CTL_RXE; - - // enable interrupts - HWREG(NVIC_EN0) = 1 << ((INT_UART0 - 16) & 31); - - return OT_ERROR_NONE; -} - -otError otPlatUartDisable(void) -{ - return OT_ERROR_NONE; -} - -otError otPlatUartSend(const uint8_t *aBuf, uint16_t aBufLength) -{ - otError error = OT_ERROR_NONE; - - otEXPECT_ACTION(sTransmitBuffer == NULL, error = OT_ERROR_BUSY); - - sTransmitBuffer = aBuf; - sTransmitLength = aBufLength; - -exit: - return error; -} - -void processReceive(void) -{ - // Copy tail to prevent multiple reads - uint16_t tail = sReceive.mTail; - - // If the data wraps around, process the first part - if (sReceive.mHead > tail) - { - otPlatUartReceived(sReceive.mBuffer + sReceive.mHead, kReceiveBufferSize - sReceive.mHead); - - // Reset the buffer mHead back to zero. - sReceive.mHead = 0; - } - - // For any data remaining, process it - if (sReceive.mHead != tail) - { - otPlatUartReceived(sReceive.mBuffer + sReceive.mHead, tail - sReceive.mHead); - - // Set mHead to the local tail we have cached - sReceive.mHead = tail; - } -} - -otError otPlatUartFlush(void) -{ - otEXPECT(sTransmitBuffer != NULL); - - for (; sTransmitLength > 0; sTransmitLength--) - { - while (HWREG(UART0_BASE + UART_O_FR) & UART_FR_TXFF) - ; - - HWREG(UART0_BASE + UART_O_DR) = *sTransmitBuffer++; - } - - sTransmitBuffer = NULL; - return OT_ERROR_NONE; - -exit: - return OT_ERROR_INVALID_STATE; -} - -void processTransmit(void) -{ - otPlatUartFlush(); - otPlatUartSendDone(); -} - -void cc2538UartProcess(void) -{ - processReceive(); - processTransmit(); -} - -void UART0IntHandler(void) -{ - uint32_t mis; - uint8_t byte; - - mis = HWREG(UART0_BASE + UART_O_MIS); - HWREG(UART0_BASE + UART_O_ICR) = mis; - - if (mis & (UART_IM_RXIM | UART_IM_RTIM)) - { - while (!(HWREG(UART0_BASE + UART_O_FR) & UART_FR_RXFE)) - { - byte = HWREG(UART0_BASE + UART_O_DR); - - // We can only write if incrementing mTail doesn't equal mHead - if (sReceive.mHead != (sReceive.mTail + 1) % kReceiveBufferSize) - { - sReceive.mBuffer[sReceive.mTail] = byte; - sReceive.mTail = (sReceive.mTail + 1) % kReceiveBufferSize; - } - } - } -} - -#if OPENTHREAD_CONFIG_ENABLE_DEBUG_UART - -int otPlatDebugUart_kbhit(void) -{ - uint32_t v; - - /* get flags */ - v = HWREG(UART1_BASE + UART_O_FR); - - /* if FIFO empty we have no data */ - return !(v & UART_FR_RXFE); -} - -int otPlatDebugUart_getc(void) -{ - int v = 1; - - /* if nothing in fifo */ - if (!otPlatDebugUart_kbhit()) - { - return -1; - } - - /* fetch */ - v = (int)HWREG(UART0_BASE + UART_O_DR); - v = (v & 0x0ff); - return v; -} - -void otPlatDebugUart_putchar_raw(int b) -{ - /* wait till not busy */ - while (HWREG(UART1_BASE + UART_O_FR) & UART_FR_TXFF) - ; - - /* write byte */ - HWREG(UART1_BASE + UART_O_DR) = ((uint32_t)(b & 0x0ff)); -} - -void cc2538DebugUartInit(void) -{ - int32_t a, b; - - // clocks - enable_uart_clocks(); - - HWREG(UART1_BASE + UART_O_CC) = 0; - - // UART1 - tx pin - // Using an RF06 Evaluation board - // http://www.ti.com/tool/cc2538dk - // PA3 => is jumper position RF1.14 - // To use these, you will require a "flying-lead" UART adapter - HWREG(IOC_PA3_SEL) = IOC_MUX_OUT_SEL_UART1_TXD; - HWREG(IOC_PA3_OVER) = IOC_OVERRIDE_OE; - HWREG(GPIO_A_BASE + GPIO_O_AFSEL) |= GPIO_PIN_3; - - // UART1 - rx pin we don't really use but we setup anyway - // PA2 => is jumper position RF1.16 - HWREG(IOC_UARTRXD_UART1) = IOC_PAD_IN_SEL_PA2; - HWREG(IOC_PA2_OVER) = IOC_OVERRIDE_DIS; - HWREG(GPIO_A_BASE + GPIO_O_AFSEL) |= GPIO_PIN_2; - - HWREG(UART1_BASE + UART_O_CC) = 0; - - // baud rate - b = (((kPlatformClock * 8) / kBaudRate) + 1) / 2; - a = b / 64; - b = b % 64; - - HWREG(UART1_BASE + UART_O_IBRD) = a; - HWREG(UART1_BASE + UART_O_FBRD) = b; - HWREG(UART1_BASE + UART_O_LCRH) = UART_CONFIG_WLEN_8 | UART_CONFIG_STOP_ONE | UART_CONFIG_PAR_NONE; - - /* NOTE: - * uart1 is not using IRQs it is tx only - * and we block when writing bytes - */ - HWREG(UART1_BASE + UART_O_CTL) = UART_CTL_UARTEN | UART_CTL_TXE | UART_CTL_RXE; -} - -#endif diff --git a/examples/platforms/simulation/platform-simulation.h b/examples/platforms/simulation/platform-simulation.h index 9379421d46a..ecd40b9eec3 100644 --- a/examples/platforms/simulation/platform-simulation.h +++ b/examples/platforms/simulation/platform-simulation.h @@ -232,6 +232,18 @@ void otSimSendUartWriteEvent(const uint8_t *aData, uint16_t aLength); */ bool platformRadioIsTransmitPending(void); +/** + * This function parses an environment variable as an unsigned 16-bit integer. + * + * If the environment variable does not exist, this function does nothing. + * If it is not a valid integer, this function will terminate the process with an error message. + * + * @param[in] aEnvName The name of the environment variable. + * @param[out] aValue A pointer to the unsigned 16-bit integer. + * + */ +void parseFromEnvAsUint16(const char *aEnvName, uint16_t *aValue); + #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE /** diff --git a/examples/platforms/simulation/radio.c b/examples/platforms/simulation/radio.c index 297495c5ce2..139864e0176 100644 --- a/examples/platforms/simulation/radio.c +++ b/examples/platforms/simulation/radio.c @@ -71,10 +71,12 @@ enum #if OPENTHREAD_SIMULATION_VIRTUAL_TIME extern int sSockFd; +extern uint16_t sPortBase; extern uint16_t sPortOffset; #else static int sTxFd = -1; static int sRxFd = -1; +static uint16_t sPortBase = 9000; static uint16_t sPortOffset = 0; static uint16_t sPort = 0; #endif @@ -166,6 +168,75 @@ static otRadioKeyType sKeyType; static int8_t GetRssi(uint16_t aChannel); +#if OPENTHREAD_SIMULATION_VIRTUAL_TIME == 0 +static uint8_t sDeniedNodeIdsBitVector[(MAX_NETWORK_SIZE + 7) / 8]; + +static bool NodeIdFilterIsConnectable(uint16_t aNodeId) +{ + uint16_t index = aNodeId - 1; + + return (sDeniedNodeIdsBitVector[index / 8] & (0x80 >> (index % 8))) == 0; +} + +static void NodeIdFilterDeny(uint16_t aNodeId) +{ + uint16_t index = aNodeId - 1; + + sDeniedNodeIdsBitVector[index / 8] |= 0x80 >> (index % 8); +} + +static void NodeIdFilterClear(void) +{ + memset(sDeniedNodeIdsBitVector, 0, sizeof(sDeniedNodeIdsBitVector)); +} + +otError ProcessNodeIdFilter(void *aContext, uint8_t aArgsLength, char *aArgs[]) +{ + OT_UNUSED_VARIABLE(aContext); + + otError error = OT_ERROR_NONE; + + otEXPECT_ACTION(aArgsLength > 0, error = OT_ERROR_INVALID_COMMAND); + + if (!strcmp(aArgs[0], "clear")) + { + otEXPECT_ACTION(aArgsLength == 1, error = OT_ERROR_INVALID_ARGS); + + NodeIdFilterClear(); + } + else if (!strcmp(aArgs[0], "deny")) + { + uint16_t nodeId; + char * endptr; + + otEXPECT_ACTION(aArgsLength == 2, error = OT_ERROR_INVALID_ARGS); + + nodeId = (uint16_t)strtol(aArgs[1], &endptr, 0); + + otEXPECT_ACTION(*endptr == '\0', error = OT_ERROR_INVALID_ARGS); + otEXPECT_ACTION(1 <= nodeId && nodeId <= MAX_NETWORK_SIZE, error = OT_ERROR_INVALID_ARGS); + + NodeIdFilterDeny(nodeId); + } + else + { + error = OT_ERROR_INVALID_COMMAND; + } + +exit: + return error; +} +#else +otError ProcessNodeIdFilter(void *aContext, uint8_t aArgsLength, char *aArgs[]) +{ + OT_UNUSED_VARIABLE(aContext); + OT_UNUSED_VARIABLE(aArgsLength); + OT_UNUSED_VARIABLE(aArgs); + + return OT_ERROR_NOT_IMPLEMENTED; +} +#endif // OPENTHREAD_SIMULATION_VIRTUAL_TIME == 0 + static bool IsTimeAfterOrEqual(uint32_t aTimeA, uint32_t aTimeB) { return (aTimeA - aTimeB) < (1U << 31); @@ -298,7 +369,7 @@ static void initFds(void) otEXPECT_ACTION((fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) != -1, perror("socket(sTxFd)")); - sPort = (uint16_t)(9000 + sPortOffset + gNodeId); + sPort = (uint16_t)(sPortBase + sPortOffset + gNodeId); sockaddr.sin_family = AF_INET; sockaddr.sin_port = htons(sPort); sockaddr.sin_addr.s_addr = inet_addr("127.0.0.1"); @@ -337,7 +408,7 @@ static void initFds(void) } sockaddr.sin_family = AF_INET; - sockaddr.sin_port = htons((uint16_t)(9000 + sPortOffset)); + sockaddr.sin_port = htons((uint16_t)(sPortBase + sPortOffset)); sockaddr.sin_addr.s_addr = inet_addr(OT_RADIO_GROUP); otEXPECT_ACTION(bind(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr)) != -1, perror("bind(sRxFd)")); @@ -356,24 +427,10 @@ static void initFds(void) void platformRadioInit(void) { #if OPENTHREAD_SIMULATION_VIRTUAL_TIME == 0 - char *offset; - - offset = getenv("PORT_OFFSET"); + parseFromEnvAsUint16("PORT_BASE", &sPortBase); - if (offset) - { - char *endptr; - - sPortOffset = (uint16_t)strtol(offset, &endptr, 0); - - if (*endptr != '\0') - { - fprintf(stderr, "Invalid PORT_OFFSET: %s\n", offset); - exit(EXIT_FAILURE); - } - - sPortOffset *= (MAX_NETWORK_SIZE + 1); - } + parseFromEnvAsUint16("PORT_OFFSET", &sPortOffset); + sPortOffset *= (MAX_NETWORK_SIZE + 1); initFds(); #endif // OPENTHREAD_SIMULATION_VIRTUAL_TIME == 0 @@ -829,7 +886,10 @@ void platformRadioProcess(otInstance *aInstance, const fd_set *aReadFdSet, const if (rval > 0) { - if (sockaddr.sin_port != htons(sPort)) + uint16_t srcPort = ntohs(sockaddr.sin_port); + uint16_t srcNodeId = srcPort - sPortOffset - sPortBase; + + if (NodeIdFilterIsConnectable(srcNodeId) && srcPort != sPort) { sReceiveFrame.mLength = (uint16_t)(rval - 1); @@ -847,7 +907,7 @@ void platformRadioProcess(otInstance *aInstance, const fd_set *aReadFdSet, const exit(EXIT_FAILURE); } } -#endif +#endif // OPENTHREAD_SIMULATION_VIRTUAL_TIME == 0 if (platformRadioIsTransmitPending()) { radioSendMessage(aInstance); @@ -870,7 +930,7 @@ void radioTransmit(struct RadioMessage *aMessage, const struct otRadioFrame *aFr sockaddr.sin_family = AF_INET; inet_pton(AF_INET, OT_RADIO_GROUP, &sockaddr.sin_addr); - sockaddr.sin_port = htons((uint16_t)(9000 + sPortOffset)); + sockaddr.sin_port = htons((uint16_t)(sPortBase + sPortOffset)); rval = sendto(sTxFd, (const char *)aMessage, 1 + aFrame->mLength, 0, (struct sockaddr *)&sockaddr, sizeof(sockaddr)); @@ -1321,3 +1381,21 @@ otError otPlatRadioGetRegion(otInstance *aInstance, uint16_t *aRegionCode) exit: return error; } + +void parseFromEnvAsUint16(const char *aEnvName, uint16_t *aValue) +{ + char *env = getenv(aEnvName); + + if (env) + { + char *endptr; + + *aValue = (uint16_t)strtol(env, &endptr, 0); + + if (*endptr != '\0') + { + fprintf(stderr, "Invalid %s: %s\n", aEnvName, env); + exit(EXIT_FAILURE); + } + } +} diff --git a/examples/platforms/simulation/virtual_time/platform-sim.c b/examples/platforms/simulation/virtual_time/platform-sim.c index fa0dd905599..20eb94717bf 100644 --- a/examples/platforms/simulation/virtual_time/platform-sim.c +++ b/examples/platforms/simulation/virtual_time/platform-sim.c @@ -61,6 +61,7 @@ char **gArguments = NULL; uint64_t sNow = 0; // microseconds int sSockFd; +uint16_t sPortBase = 9000; uint16_t sPortOffset; static void handleSignal(int aSignal) @@ -78,7 +79,7 @@ void otSimSendEvent(const struct Event *aEvent) memset(&sockaddr, 0, sizeof(sockaddr)); sockaddr.sin_family = AF_INET; inet_pton(AF_INET, "127.0.0.1", &sockaddr.sin_addr); - sockaddr.sin_port = htons(9000 + sPortOffset); + sockaddr.sin_port = htons(sPortBase + sPortOffset); rval = sendto(sSockFd, aEvent, offsetof(struct Event, mData) + aEvent->mDataLength, 0, (struct sockaddr *)&sockaddr, sizeof(sockaddr)); @@ -176,28 +177,15 @@ otError otPlatUartFlush(void) static void socket_init(void) { struct sockaddr_in sockaddr; - char * offset; memset(&sockaddr, 0, sizeof(sockaddr)); sockaddr.sin_family = AF_INET; - offset = getenv("PORT_OFFSET"); + parseFromEnvAsUint16("PORT_BASE", &sPortBase); - if (offset) - { - char *endptr; - - sPortOffset = (uint16_t)strtol(offset, &endptr, 0); - - if (*endptr != '\0') - { - fprintf(stderr, "Invalid PORT_OFFSET: %s\n", offset); - exit(EXIT_FAILURE); - } - - sPortOffset *= (MAX_NETWORK_SIZE + 1); - } + parseFromEnvAsUint16("PORT_OFFSET", &sPortOffset); + sPortOffset *= (MAX_NETWORK_SIZE + 1); - sockaddr.sin_port = htons((uint16_t)(9000 + sPortOffset + gNodeId)); + sockaddr.sin_port = htons((uint16_t)(sPortBase + sPortOffset + gNodeId)); sockaddr.sin_addr.s_addr = INADDR_ANY; sSockFd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); diff --git a/tools/harness-simulation/posix/config.py b/examples/platforms/zephyr/CMakeLists.txt similarity index 87% rename from tools/harness-simulation/posix/config.py rename to examples/platforms/zephyr/CMakeLists.txt index 8e12d8d8403..dcdad63b8ce 100644 --- a/tools/harness-simulation/posix/config.py +++ b/examples/platforms/zephyr/CMakeLists.txt @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # # Copyright (c) 2022, The OpenThread Authors. # All rights reserved. @@ -27,10 +26,5 @@ # POSSIBILITY OF SUCH DAMAGE. # -MAX_NODES_NUM = 64 -MAX_SNIFFER_NUM = 2 -SNIFFER_SERVER_PORT_BASE = 50051 - -OT_PATH = '/home/pi/work/src/openthread-pr' -OTBR_DOCKER_IMAGE = 'otbr-reference-device-1.2' -OTBR_DOCKER_NAME_PREFIX = 'otbr_' +# Intentionally empty, the file is only needed to enable "zephyr" target +# as OT platform for CMake diff --git a/examples/platforms/zephyr/README.md b/examples/platforms/zephyr/README.md new file mode 100644 index 00000000000..b6e9828823d --- /dev/null +++ b/examples/platforms/zephyr/README.md @@ -0,0 +1,3 @@ +The OpenThread stack is integrated with ZephyrOS and nRF Connect SDK. + +See the [Zephyr's OpenThread platform](https://github.com/zephyrproject-rtos/zephyr/tree/main/modules/openthread) and [CLI example](https://github.com/nrfconnect/sdk-nrf/tree/main/samples/openthread/cli) for more information about the integration. diff --git a/include/openthread/cli.h b/include/openthread/cli.h index 3f463d797ab..9b07e2fefd0 100644 --- a/include/openthread/cli.h +++ b/include/openthread/cli.h @@ -52,9 +52,9 @@ extern "C" { typedef struct otCliCommand { const char *mName; ///< A pointer to the command string. - void (*mCommand)(void * aContext, - uint8_t aArgsLength, - char * aArgs[]); ///< A function pointer to process the command. + otError (*mCommand)(void * aContext, + uint8_t aArgsLength, + char * aArgs[]); ///< A function pointer to process the command. } otCliCommand; /** diff --git a/include/openthread/instance.h b/include/openthread/instance.h index e5460a85e7c..0b47bbe9939 100644 --- a/include/openthread/instance.h +++ b/include/openthread/instance.h @@ -53,7 +53,7 @@ extern "C" { * @note This number versions both OpenThread platform and user APIs. * */ -#define OPENTHREAD_API_VERSION (241) +#define OPENTHREAD_API_VERSION (246) /** * @addtogroup api-instance diff --git a/include/openthread/nat64.h b/include/openthread/nat64.h index 2ab22058419..09a26b87cc6 100644 --- a/include/openthread/nat64.h +++ b/include/openthread/nat64.h @@ -88,13 +88,151 @@ typedef struct otIp4Cidr uint8_t mLength; } otIp4Cidr; +/** + * Represents the counters for NAT64. + * + */ +typedef struct otNat64Counters +{ + uint64_t m4To6Packets; ///< Number of packets translated from IPv4 to IPv6. + uint64_t m4To6Bytes; ///< Sum of size of packets translated from IPv4 to IPv6. + uint64_t m6To4Packets; ///< Number of packets translated from IPv6 to IPv4. + uint64_t m6To4Bytes; ///< Sum of size of packets translated from IPv6 to IPv4. +} otNat64Counters; + +/** + * Represents the counters for the protocols supported by NAT64. + * + */ +typedef struct otNat64ProtocolCounters +{ + otNat64Counters mTotal; ///< Counters for sum of all protocols. + otNat64Counters mIcmp; ///< Counters for ICMP and ICMPv6. + otNat64Counters mUdp; ///< Counters for UDP. + otNat64Counters mTcp; ///< Counters for TCP. +} otNat64ProtocolCounters; + +/** + * Packet drop reasons. + * + */ +typedef enum otNat64DropReason +{ + OT_NAT64_DROP_REASON_UNKNOWN = 0, ///< Packet drop for unknown reasons. + OT_NAT64_DROP_REASON_ILLEGAL_PACKET, ///< Packet drop due to failed to parse the datagram. + OT_NAT64_DROP_REASON_UNSUPPORTED_PROTO, ///< Packet drop due to unsupported IP protocol. + OT_NAT64_DROP_REASON_NO_MAPPING, ///< Packet drop due to no mappings found or mapping pool exhausted. + //--- + OT_NAT64_DROP_REASON_COUNT, +} otNat64DropReason; + +/** + * Represents the counters of dropped packets due to errors when handling NAT64 packets. + * + */ +typedef struct otNat64ErrorCounters +{ + uint64_t mCount4To6[OT_NAT64_DROP_REASON_COUNT]; ///< Errors translating IPv4 packets. + uint64_t mCount6To4[OT_NAT64_DROP_REASON_COUNT]; ///< Errors translating IPv6 packets. +} otNat64ErrorCounters; + +/** + * Gets NAT64 translator counters. + * + * The counter is counted since the instance initialized. + * + * Available when `OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE` is enabled. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[out] aCounters A pointer to an `otNat64Counters` where the counters of NAT64 translator will be placed. + * + */ +void otNat64GetCounters(otInstance *aInstance, otNat64ProtocolCounters *aCounters); + +/** + * Gets the NAT64 translator error counters. + * + * The counters are initialized to zero when the OpenThread instance is initialized. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[out] aCounters A pointer to an `otNat64Counters` where the counters of NAT64 translator will be placed. + * + */ +void otNat64GetErrorCounters(otInstance *aInstance, otNat64ErrorCounters *aCounters); + +/** + * Represents an address mapping record for NAT64. + * + * @note The counters will be reset for each mapping session even for the same address pair. Applications can use `mId` + * to identify different sessions to calculate the packets correctly. + * + */ +typedef struct otNat64AddressMapping +{ + uint64_t mId; ///< The unique id for a mapping session. + + otIp4Address mIp4; ///< The IPv4 address of the mapping. + otIp6Address mIp6; ///< The IPv6 address of the mapping. + uint32_t mRemainingTimeMs; ///< Remaining time before expiry in milliseconds. + + otNat64ProtocolCounters mCounters; +} otNat64AddressMapping; + +/** + * Used to iterate through NAT64 address mappings. + * + * The fields in this type are opaque (intended for use by OpenThread core only) and therefore should not be + * accessed or used by caller. + * + * Before using an iterator, it MUST be initialized using `otNat64AddressMappingIteratorInit()`. + * + */ +typedef struct otNat64AddressMappingIterator +{ + void *mPtr; +} otNat64AddressMappingIterator; + +/** + * Initializes an `otNat64AddressMappingIterator`. + * + * An iterator MUST be initialized before it is used. + * + * An iterator can be initialized again to restart from the beginning of the mapping info. + * + * @param[in] aInstance The OpenThread instance. + * @param[out] aIterator A pointer to the iterator to initialize. + * + */ +void otNat64InitAddressMappingIterator(otInstance *aInstance, otNat64AddressMappingIterator *aIterator); + +/** + * Gets the next AddressMapping info (using an iterator). + * + * Available when `OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE` is enabled. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[in,out] aIterator A pointer to the iterator. On success the iterator will be updated to point to next + * NAT64 address mapping record. To get the first entry the iterator should be set to + * OT_NAT64_ADDRESS_MAPPING_ITERATOR_INIT. + * @param[out] aMapping A pointer to an `otNat64AddressMapping` where information of next NAT64 address + * mapping record is placed (on success). + * + * @retval OT_ERROR_NONE Successfully found the next NAT64 address mapping info (@p aMapping was successfully + * updated). + * @retval OT_ERROR_NOT_FOUND No subsequent NAT64 address mapping info was found. + * + */ +otError otNat64GetNextAddressMapping(otInstance * aInstance, + otNat64AddressMappingIterator *aIterator, + otNat64AddressMapping * aMapping); + /** * Allocate a new message buffer for sending an IPv4 message to the NAT64 translator. * * Message buffers allocated by this function will have 20 bytes (difference between the size of IPv6 headers * and IPv4 header sizes) reserved. * - * This function is available only when `OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE` is enabled. + * Available when `OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE` is enabled. * * @note If @p aSettings is `NULL`, the link layer security is enabled and the message priority is set to * OT_MESSAGE_PRIORITY_NORMAL by default. @@ -174,6 +312,17 @@ typedef void (*otNat64ReceiveIp4Callback)(otMessage *aMessage, void *aContext); */ void otNat64SetReceiveIp4Callback(otInstance *aInstance, otNat64ReceiveIp4Callback aCallback, void *aContext); +/** + * Gets the IPv4 CIDR configured in the NAT64 translator. + * + * Available when `OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE` is enabled. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[out] aCidr A pointer to an otIp4Cidr. Where the CIDR will be filled. + * + */ +otError otNat64GetCidr(otInstance *aInstance, otIp4Cidr *aCidr); + /** * Test if two IPv4 addresses are the same. * @@ -200,6 +349,67 @@ bool otIp4IsAddressEqual(const otIp4Address *aFirst, const otIp4Address *aSecond */ void otIp4ExtractFromIp6Address(uint8_t aPrefixLength, const otIp6Address *aIp6Address, otIp4Address *aIp4Address); +#define OT_IP4_ADDRESS_STRING_SIZE 17 ///< Length of 000.000.000.000 plus a suffix NUL + +/** + * Converts the address to a string. + * + * The string format uses quad-dotted notation of four bytes in the address (e.g., "127.0.0.1"). + * + * If the resulting string does not fit in @p aBuffer (within its @p aSize characters), the string will be + * truncated but the outputted string is always null-terminated. + * + * @param[in] aAddress A pointer to an IPv4 address (MUST NOT be NULL). + * @param[out] aBuffer A pointer to a char array to output the string (MUST NOT be `nullptr`). + * @param[in] aSize The size of @p aBuffer (in bytes). + * + */ +void otIp4AddressToString(const otIp4Address *aAddress, char *aBuffer, uint16_t aSize); + +#define OT_IP4_CIDR_STRING_SIZE 20 ///< Length of 000.000.000.000/00 plus a suffix NUL + +/** + * Converts the IPv4 CIDR to a string. + * + * The string format uses quad-dotted notation of four bytes in the address with the length of prefix (e.g., + * "127.0.0.1/32"). + * + * If the resulting string does not fit in @p aBuffer (within its @p aSize characters), the string will be + * truncated but the outputted string is always null-terminated. + * + * @param[in] aCidr A pointer to an IPv4 CIDR (MUST NOT be NULL). + * @param[out] aBuffer A pointer to a char array to output the string (MUST NOT be `nullptr`). + * @param[in] aSize The size of @p aBuffer (in bytes). + * + */ +void otIp4CidrToString(const otIp4Cidr *aCidr, char *aBuffer, uint16_t aSize); + +/** + * Converts a human-readable IPv4 address string into a binary representation. + * + * @param[in] aString A pointer to a NULL-terminated string. + * @param[out] aAddress A pointer to an IPv4 address. + * + * @retval OT_ERROR_NONE Successfully parsed the string. + * @retval OT_ERROR_INVALID_ARGS Failed to parse the string. + * + */ +otError otIp4AddressFromString(const char *aString, otIp4Address *aAddress); + +/** + * Sets the IPv6 address by performing NAT64 address translation from the preferred NAT64 prefix and the given IPv4 + * address as specified in RFC 6052. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[in] aIp4Address A pointer to the IPv4 address to translate to IPv6. + * @param[out] aIp6Address A pointer to the synthesized IPv6 address. + * + * @returns OT_ERROR_NONE Successfully synthesized the IPv6 address from NAT64 prefix and IPv4 address. + * @returns OT_ERROR_INVALID_STATE No valid NAT64 prefix in the network data. + * + */ +otError otNat64SynthersizeIp6Address(otInstance *aInstance, const otIp4Address *aIp4Address, otIp6Address *aIp6Address); + /** * @} * diff --git a/include/openthread/srp_server.h b/include/openthread/srp_server.h index fba71749b03..4c928e0d261 100644 --- a/include/openthread/srp_server.h +++ b/include/openthread/srp_server.h @@ -132,14 +132,14 @@ enum }; /** - * Represents the state of an SRP server + * This enumeration represents the state of the SRP server. * */ typedef enum { OT_SRP_SERVER_STATE_DISABLED = 0, ///< The SRP server is disabled. - OT_SRP_SERVER_STATE_RUNNING = 1, ///< The SRP server is running. - OT_SRP_SERVER_STATE_STOPPED = 2, ///< The SRP server is stopped. + OT_SRP_SERVER_STATE_RUNNING = 1, ///< The SRP server is enabled and running. + OT_SRP_SERVER_STATE_STOPPED = 2, ///< The SRP server is enabled but stopped. } otSrpServerState; /** @@ -302,12 +302,49 @@ otError otSrpServerSetAnycastModeSequenceNumber(otInstance *aInstance, uint8_t a /** * This function enables/disables the SRP server. * + * On a Border Router, it is recommended to use `otSrpServerSetAutoEnableMode()` instead. + * * @param[in] aInstance A pointer to an OpenThread instance. * @param[in] aEnabled A boolean to enable/disable the SRP server. * */ void otSrpServerSetEnabled(otInstance *aInstance, bool aEnabled); +/** + * This function enables/disables the auto-enable mode on SRP server. + * + * This function requires `OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE` feature. + * + * When this mode is enabled, the Border Routing Manager controls if/when to enable or disable the SRP server. + * SRP sever is auto-enabled if/when Border Routing is started and it is done with the initial prefix and route + * configurations (when the OMR and on-link prefixes are determined, advertised in emitted Router Advertisement message + * on infrastructure side and published in the Thread Network Data). The SRP server is auto-disabled if/when BR is + * stopped (e.g., if the infrastructure network interface is brought down or if BR gets detached). + * + * This mode can be disabled by a `otSrpServerSetAutoEnableMode()` call with @p aEnabled set to `false` or if the SRP + * server is explicitly enabled or disabled by a call to `otSrpServerSetEnabled()` function. Disabling auto-enable mode + * using `otSrpServerSetAutoEnableMode(false)` will not change the current state of SRP sever (e.g., if it is enabled + * it stays enabled). + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[in] aEnbaled A boolean to enable/disable the auto-enable mode. + * + */ +void otSrpServerSetAutoEnableMode(otInstance *aInstance, bool aEnabled); + +/** + * This function indicates whether the auto-enable mode is enabled or disabled. + * + * This function requires `OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE` feature. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * + * @retval TRUE The auto-enable mode is enabled. + * @retval FALSE The auto-enable mode is disabled. + * + */ +bool otSrpServerIsAutoEnableMode(otInstance *aInstance); + /** * This function returns SRP server TTL configuration. * diff --git a/include/openthread/tcp_ext.h b/include/openthread/tcp_ext.h index 5c0ddc678e5..75808abe00d 100644 --- a/include/openthread/tcp_ext.h +++ b/include/openthread/tcp_ext.h @@ -88,10 +88,10 @@ extern "C" { */ typedef struct otTcpCircularSendBuffer { - const uint8_t *mDataBuffer; ///< Pointer to data in the circular send buffer - size_t mCapacity; ///< Length of the circular send buffer - size_t mStartIndex; ///< Index of the first valid byte in the send buffer - size_t mCapacityUsed; ///< Number of bytes stored in the send buffer + uint8_t *mDataBuffer; ///< Pointer to data in the circular send buffer + size_t mCapacity; ///< Length of the circular send buffer + size_t mStartIndex; ///< Index of the first valid byte in the send buffer + size_t mCapacityUsed; ///< Number of bytes stored in the send buffer otLinkedBuffer mSendLinks[2]; uint8_t mFirstSendLinkIndex; @@ -107,6 +107,15 @@ typedef struct otTcpCircularSendBuffer */ void otTcpCircularSendBufferInitialize(otTcpCircularSendBuffer *aSendBuffer, void *aDataBuffer, size_t aCapacity); +/** + * This enumeration defines flags passed to @p otTcpCircularSendBufferWrite. + * + */ +enum +{ + OT_TCP_CIRCULAR_SEND_BUFFER_WRITE_MORE_TO_COME = 1 << 0, +}; + /** * Sends out data on a TCP endpoint, using the provided TCP circular send * buffer to manage buffering. @@ -136,15 +145,17 @@ void otTcpCircularSendBufferInitialize(otTcpCircularSendBuffer *aSendBuffer, voi * @param[in] aLength The length of the data pointed to by @p aData to copy into the TCP circular send buffer. * @param[out] aWritten Populated with the amount of data copied into the send buffer, which might be less than * @p aLength if the send buffer reaches capacity. + * @param[in] aFlags Flags specifying options for this operation (see enumeration above). * * @returns OT_ERROR_NONE Successfully copied data into the send buffer and sent it on the TCP endpoint. * @returns OT_ERROR_FAILED Failed to send out data on the TCP endpoint. */ otError otTcpCircularSendBufferWrite(otTcpEndpoint * aEndpoint, otTcpCircularSendBuffer *aSendBuffer, - void * aData, + const void * aData, size_t aLength, - size_t * aWritten); + size_t * aWritten, + uint32_t aFlags); /** * Performs circular-send-buffer-specific handling in the otTcpForwardProgress @@ -175,7 +186,7 @@ void otTcpCircularSendBufferHandleForwardProgress(otTcpCircularSendBuffer *aSend * * @return The amount of free space in the send buffer. */ -size_t otTcpCircularSendBufferFreeSpace(otTcpCircularSendBuffer *aSendBuffer); +size_t otTcpCircularSendBufferGetFreeSpace(const otTcpCircularSendBuffer *aSendBuffer); /** * Forcibly discards all data in the circular send buffer. diff --git a/script/check-arm-build b/script/check-arm-build index 2f302d625f5..75b36606332 100755 --- a/script/check-arm-build +++ b/script/check-arm-build @@ -29,10 +29,77 @@ set -euxo pipefail +readonly OT_TMP_DIR=/tmp/ot-arm-build-cmake +readonly OT_SHA_NEW=${GITHUB_SHA:-$(git rev-parse HEAD)} + +build_nrf52840() +{ + local options=( + "-DOT_ANYCAST_LOCATOR=ON" + "-DOT_BACKBONE_ROUTER=ON" + "-DOT_BORDER_AGENT=ON" + "-DOT_BORDER_ROUTER=ON" + "-DOT_CHANNEL_MANAGER=ON" + "-DOT_CHANNEL_MONITOR=ON" + "-DOT_CHILD_SUPERVISION=ON" + "-DOT_COAP=ON" + "-DOT_COAPS=ON" + "-DOT_COMMISSIONER=ON" + "-DOT_CSL_RECEIVER=ON" + "-DOT_DATASET_UPDATER=ON" + "-DOT_DHCP6_CLIENT=ON" + "-DOT_DHCP6_SERVER=ON" + "-DOT_DIAGNOSTIC=ON" + "-DOT_DNSSD_SERVER=ON" + "-DOT_DNS_CLIENT=ON" + "-DOT_DUA=ON" + "-DOT_ECDSA=ON" + "-DOT_FULL_LOGS=ON" + "-DOT_JAM_DETECTION=ON" + "-DOT_JOINER=ON" + "-DOT_LINK_METRICS_INITIATOR=ON" + "-DOT_LINK_METRICS_SUBJECT=ON" + "-DOT_LINK_RAW=ON" + "-DOT_MAC_FILTER=ON" + "-DOT_MESSAGE_USE_HEAP=ON" + "-DOT_MLR=ON" + "-DOT_MTD_NETDIAG=ON" + "-DOT_NETDATA_PUBLISHER=ON" + "-DOT_PING_SENDER=ON" + "-DOT_SERVICE=ON" + "-DOT_SLAAC=ON" + "-DOT_SNTP_CLIENT=ON" + "-DOT_SRP_CLIENT=ON" + "-DOT_SRP_SERVER=ON" + "-DOT_THREAD_VERSION=1.3" + "-DOT_TIME_SYNC=ON" + "-DOT_UDP_FORWARD=ON" + "-DOT_UPTIME=ON" + ) + + rm -rf "${OT_TMP_DIR}" + + script/git-tool clone https://github.com/openthread/ot-nrf528xx.git "${OT_TMP_DIR}" + rm -rf "${OT_TMP_DIR}/openthread/*" + git archive "${OT_SHA_NEW}" | tar x -C "${OT_TMP_DIR}/openthread" + + cd "${OT_TMP_DIR}" + script/build nrf52840 UART_trans "${options[@]}" +} + main() { - "$(dirname "$0")"/check-arm-build-autotools - "$(dirname "$0")"/check-arm-build-cmake + export CPPFLAGS="${CPPFLAGS:-} -DNDEBUG" + + if [[ $# == 0 ]]; then + build_nrf52840 + return 0 + fi + + while [[ $# != 0 ]]; do + "build_$1" + shift + done } main "$@" diff --git a/script/check-arm-build-autotools b/script/check-arm-build-autotools deleted file mode 100755 index e994c202525..00000000000 --- a/script/check-arm-build-autotools +++ /dev/null @@ -1,71 +0,0 @@ -#!/bin/bash -# -# Copyright (c) 2020, The OpenThread Authors. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# 3. Neither the name of the copyright holder nor the -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# - -set -euxo pipefail - -reset_source() -{ - rm -rf build output tmp -} - -build_cc2538() -{ - local options=( - "COMMISSIONER=1" - "DHCP6_CLIENT=1" - "DHCP6_SERVER=1" - "DNS_CLIENT=1" - "JOINER=1" - "SLAAC=1" - # cc2538 does not have enough resources to support Thread 1.3 - "THREAD_VERSION=1.1" - ) - - reset_source - make -f examples/Makefile-cc2538 "${options[@]}" -} - -main() -{ - ./bootstrap - - export CPPFLAGS="${CPPFLAGS:-} -DNDEBUG" - - if [[ $# == 0 ]]; then - build_cc2538 - return 0 - fi - - while [[ $# != 0 ]]; do - "build_$1" - shift - done -} - -main "$@" diff --git a/script/check-arm-build-cmake b/script/check-arm-build-cmake deleted file mode 100755 index 3ee62b06e6d..00000000000 --- a/script/check-arm-build-cmake +++ /dev/null @@ -1,77 +0,0 @@ -#!/bin/bash -# -# Copyright (c) 2020, The OpenThread Authors. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# 3. Neither the name of the copyright holder nor the -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# - -set -euxo pipefail - -readonly OT_BUILDDIR="$(pwd)/build" - -readonly OT_COMMON_OPTIONS=( - "-DOT_COMPILE_WARNING_AS_ERROR=ON" -) - -readonly OT_BASIC_CHECK_OPTIONS=( - "-DOT_COMMISSIONER=ON" - "-DOT_DHCP6_CLIENT=ON" - "-DOT_DHCP6_SERVER=ON" - "-DOT_DNS_CLIENT=ON" - "-DOT_JOINER=ON" -) - -reset_source() -{ - rm -rf "$OT_BUILDDIR" -} - -build_cc2538() -{ - local options=( - # cc2538 does not have enough resources to support Thread 1.3 - "-DOT_THREAD_VERSION=1.1" - ) - - reset_source - "$(dirname "$0")"/cmake-build cc2538 "${OT_COMMON_OPTIONS[@]}" "${OT_BASIC_CHECK_OPTIONS[@]}" "${options[@]}" -} - -main() -{ - export CPPFLAGS="${CPPFLAGS:-} -DNDEBUG" - - if [[ $# == 0 ]]; then - build_cc2538 - return 0 - fi - - while [[ $# != 0 ]]; do - "build_$1" - shift - done -} - -main "$@" diff --git a/script/cmake-build b/script/cmake-build index 215e197ecb1..3b592e52b8b 100755 --- a/script/cmake-build +++ b/script/cmake-build @@ -66,7 +66,7 @@ OT_CMAKE_NINJA_TARGET=${OT_CMAKE_NINJA_TARGET:-} OT_SRCDIR="$(cd "$(dirname "$0")"/.. && pwd)" readonly OT_SRCDIR -readonly OT_PLATFORMS=(cc2538 simulation posix) +readonly OT_PLATFORMS=(simulation posix) readonly OT_POSIX_BUILD_TYPES=(multipan_rcp) readonly OT_POSIX_SIM_COMMON_OPTIONS=( "-DOT_ANYCAST_LOCATOR=ON" @@ -184,9 +184,6 @@ main() local_options=("-DOT_LINK_RAW=ON") options+=("${OT_POSIX_SIM_COMMON_OPTIONS[@]}" "${local_options[@]}") ;; - cc2538) - options+=("-DCMAKE_TOOLCHAIN_FILE=examples/platforms/${platform}/arm-none-eabi.cmake" "-DCMAKE_BUILD_TYPE=MinSizeRel") - ;; *) options+=("-DCMAKE_TOOLCHAIN_FILE=examples/platforms/${platform}/arm-none-eabi.cmake") ;; diff --git a/src/cli/README.md b/src/cli/README.md index 8a2201af168..16408874261 100644 --- a/src/cli/README.md +++ b/src/cli/README.md @@ -70,6 +70,7 @@ Done - [mlr](#mlr-reg-ipaddr--timeout) - [mode](#mode) - [multiradio](#multiradio) +- [nat64](#nat64-cidr) - [neighbor](#neighbor-list) - [netdata](README_NETDATA.md) - [netstat](#netstat) @@ -82,7 +83,7 @@ Done - [parent](#parent) - [parentpriority](#parentpriority) - [partitionid](#partitionid) -- [ping](#ping--i-source-ipaddr-size-count-interval-hoplimit-timeout) +- [ping](#ping-async--i-source-ipaddr-size-count-interval-hoplimit-timeout) - [pollperiod](#pollperiod-pollperiod) - [preferrouterid](#preferrouterid-routerid) - [prefix](#prefix) @@ -1835,6 +1836,61 @@ This command is only available when device supports more than one radio link. Done ``` +### nat64 cidr + +Gets the IPv4 configured CIDR in the NAT64 translator. + +This command is only available when device enables NAT64 translator. + +```bash +> nat64 cidr +192.168.64.0/24 +Done +``` + +### nat64 mappings + +Get the NAT64 translator mappings. + +This command is only available when device enables NAT64 translator. + +```bash +> nat64 mappings +| | Address | | 4 to 6 | 6 to 4 | ++----------+---------------------------+--------+--------------+--------------+ +| ID | IPv6 | IPv4 | Expiry | Pkts | Bytes | Pkts | Bytes | ++----------+------------+--------------+--------+------+-------+------+-------+ +| 00021cb9 | fdc7::df79 | 192.168.64.2 | 7196s | 6 | 456 | 11 | 1928 | +| | TCP | 0 | 0 | 0 | 0 | +| | UDP | 1 | 136 | 16 | 1608 | +| | ICMP | 5 | 320 | 5 | 320 | +``` + +### nat64 counters + +Get the NAT64 translator packet and error counters. + +This command is only available when device enables NAT64 translator. + +```bash +> nat64 counters +| | 4 to 6 | 6 to 4 | ++---------------+-------------------------+-------------------------+ +| Protocol | Pkts | Bytes | Pkts | Bytes | ++---------------+----------+--------------+----------+--------------+ +| Total | 11 | 704 | 11 | 704 | +| TCP | 0 | 0 | 0 | 0 | +| UDP | 0 | 0 | 0 | 0 | +| ICMP | 11 | 704 | 11 | 704 | +| Errors | Pkts | Pkts | ++---------------+-------------------------+-------------------------+ +| Total | 8 | 4 | +| Illegal Pkt | 0 | 0 | +| Unsup Proto | 0 | 0 | +| No Mapping | 2 | 0 | +Done +``` + ### neighbor list List RLOC16 of neighbors. @@ -2067,10 +2123,11 @@ Set the preferred Thread Leader Partition ID. Done ``` -### ping \[-I source\] \ \[size\] \[count\] \[interval\] \[hoplimit\] \[timeout\] +### ping \[async\] \[-I source\] \ \[size\] \[count\] \[interval\] \[hoplimit\] \[timeout\] Send an ICMPv6 Echo Request. +- async: Use the non-blocking mode. New commands are allowed before the ping process terminates. - source: The source IPv6 address of the echo request. - size: The number of data bytes to be sent. - count: The number of ICMPv6 Echo Requests to be sent. @@ -2090,6 +2147,18 @@ Done Done ``` +The address can be an IPv4 address, which will be synthesized to an IPv6 address using the preferred NAT64 prefix from the network data. + +> Note: The command will return `InvalidState` when the preferred NAT64 prefix is unavailable. + +```bash +> ping 172.17.0.1 +Pinging synthesized IPv6 address: fdde:ad00:beef:2:0:0:ac11:1 +> 16 bytes from fdde:ad00:beef:2:0:0:ac11:1: icmp_seq=5 hlim=64 time=0ms +1 packets transmitted, 1 packets received. Packet loss = 0.0%. Round-trip min/avg/max = 0/0.0/0 ms. +Done +``` + ### ping stop Stop sending ICMPv6 Echo Requests. diff --git a/src/cli/README_TCP.md b/src/cli/README_TCP.md index c1aba71d05c..eb42fbaff50 100644 --- a/src/cli/README_TCP.md +++ b/src/cli/README_TCP.md @@ -109,7 +109,7 @@ Establishes a connection with the specified peer. If the connection establishment is successful, the resulting TCP connection is associated with the example TCP endpoint. -- ip: the peer's IPv6 address. +- ip: the peer's IP address. - port: the peer's TCP port. ```bash @@ -118,6 +118,16 @@ Done TCP: Connection established ``` +The address can be an IPv4 address, which will be synthesized to an IPv6 address using the preferred NAT64 prefix from the network data. + +> Note: The command will return `InvalidState` when the preferred NAT64 prefix is unavailable. + +```bash +> tcp connect 172.17.0.1 1234 +Connecting to synthesized IPv6 address: fdde:ad00:beef:2:0:0:ac11:1 +Done +``` + ### deinit Deinitializes the example TCP listener and the example TCP endpoint. diff --git a/src/cli/README_UDP.md b/src/cli/README_UDP.md index 51cd8cc5b1f..14444cdaf14 100644 --- a/src/cli/README_UDP.md +++ b/src/cli/README_UDP.md @@ -96,7 +96,7 @@ Done Specifies the peer with which the socket is to be associated. -- ip: the peer's IPv6 address. +- ip: the peer's IP address. - port: the peer's UDP port. ```bash @@ -104,6 +104,16 @@ Specifies the peer with which the socket is to be associated. Done ``` +The address can be an IPv4 address, which will be synthesized to an IPv6 address using the preferred NAT64 prefix from the network data. + +> Note: The command will return `InvalidState` when the preferred NAT64 prefix is unavailable. + +```bash +> udp connect 172.17.0.1 1234 +Connecting to synthesized IPv6 address: fdde:ad00:beef:2:0:0:ac11:1 +Done +``` + ### linksecurity Indicates whether the link security is enabled or disabled. @@ -145,7 +155,7 @@ Done Send a UDP message. -- ip: the IPv6 destination address. +- ip: the destination address. - port: the UDP destination port. - message: the message to send. @@ -154,6 +164,16 @@ Send a UDP message. Done ``` +The address can be an IPv4 address, which will be synthesized to an IPv6 address using the preferred NAT64 prefix from the network data. + +> Note: The command will return `InvalidState` when the preferred NAT64 prefix is unavailable. + +```bash +> udp send 172.17.0.1 1234 +Sending to synthesized IPv6 address: fdde:ad00:beef:2:0:0:ac11:1 +Done +``` + ### send \ \ \ \ Send a few bytes over UDP. diff --git a/src/cli/cli.cpp b/src/cli/cli.cpp index 2dfd8450ef8..ecc836b875b 100644 --- a/src/cli/cli.cpp +++ b/src/cli/cli.cpp @@ -87,6 +87,9 @@ #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE #include #endif +#if OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE +#include +#endif #include "common/new.hpp" #include "common/string.hpp" @@ -462,6 +465,31 @@ const char *Interpreter::PreferenceToString(signed int aPreference) return str; } +otError Interpreter::ParseToIp6Address(otInstance * aInstance, + const Arg & aArg, + otIp6Address &aAddress, + bool & aSynthesized) +{ + Error error = kErrorNone; + + VerifyOrExit(!aArg.IsEmpty(), error = OT_ERROR_INVALID_ARGS); + error = aArg.ParseAsIp6Address(aAddress); + aSynthesized = false; + if (error != kErrorNone) + { + // It might be an IPv4 address, let's have a try. + otIp4Address ip4Address; + + // Do not touch the error value if we failed to parse it as an IPv4 address. + SuccessOrExit(aArg.ParseAsIp4Address(ip4Address)); + SuccessOrExit(error = otNat64SynthersizeIp6Address(aInstance, &ip4Address, &aAddress)); + aSynthesized = true; + } + +exit: + return error; +} + #if OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE template <> otError Interpreter::Process(Arg aArgs[]) { @@ -717,6 +745,213 @@ template <> otError Interpreter::Process(Arg aArgs[]) } #endif // OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE +#if OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE +template <> otError Interpreter::Process(Arg aArgs[]) +{ + otError error = OT_ERROR_NONE; + + if (aArgs[0].IsEmpty()) + { + ExitNow(error = OT_ERROR_INVALID_COMMAND); + } + /** + * @cli nat64 cidr + * @code + * nat64 cidr + * 192.168.64.0/24 + * Done + * @endcode + * @par api_copy + * #otNat64GetCidr + * + */ + else if (aArgs[0] == "cidr") + { + otIp4Cidr cidr; + char cidrString[OT_IP4_CIDR_STRING_SIZE]; + + SuccessOrExit(error = otNat64GetCidr(GetInstancePtr(), &cidr)); + otIp4CidrToString(&cidr, cidrString, sizeof(cidrString)); + OutputLine("%s", cidrString); + } + /** + * @cli nat64 mappings + * @code + * nat64 mappings + * | | Address | | 4 to 6 | 6 to 4 | + * +----------+---------------------------+--------+--------------+--------------+ + * | ID | IPv6 | IPv4 | Expiry | Pkts | Bytes | Pkts | Bytes | + * +----------+------------+--------------+--------+------+-------+------+-------+ + * | 00021cb9 | fdc7::df79 | 192.168.64.2 | 7196s | 6 | 456 | 11 | 1928 | + * | | TCP | 0 | 0 | 0 | 0 | + * | | UDP | 1 | 136 | 16 | 1608 | + * | | ICMP | 5 | 320 | 5 | 320 | + * @endcode + * @par api_copy + * #otNat64GetNextAddressMapping + * + */ + else if (aArgs[0] == "mappings") + { + otNat64AddressMappingIterator iterator; + otNat64AddressMapping mapping; + + static const char *const kNat64StatusLevel1Title[] = {"", "Address", "", "4 to 6", "6 to 4"}; + + static const uint8_t kNat64StatusLevel1ColumnWidths[] = { + 18, 61, 8, 25, 25, + }; + + static const char *const kNat64StatusTableHeader[] = { + "ID", "IPv6", "IPv4", "Expiry", "Pkts", "Bytes", "Pkts", "Bytes", + }; + + static const uint8_t kNat64StatusTableColumnWidths[] = { + 18, 42, 18, 8, 10, 14, 10, 14, + }; + + OutputTableHeader(kNat64StatusLevel1Title, kNat64StatusLevel1ColumnWidths); + OutputTableHeader(kNat64StatusTableHeader, kNat64StatusTableColumnWidths); + + otNat64InitAddressMappingIterator(GetInstancePtr(), &iterator); + while (otNat64GetNextAddressMapping(GetInstancePtr(), &iterator, &mapping) == OT_ERROR_NONE) + { + char ip4AddressString[OT_IP4_ADDRESS_STRING_SIZE]; + char ip6AddressString[OT_IP6_PREFIX_STRING_SIZE]; + + otIp6AddressToString(&mapping.mIp6, ip6AddressString, sizeof(ip6AddressString)); + otIp4AddressToString(&mapping.mIp4, ip4AddressString, sizeof(ip4AddressString)); + + OutputFormat("| %016llx ", mapping.mId); + OutputFormat("| %40s ", ip6AddressString); + OutputFormat("| %16s ", ip4AddressString); + OutputFormat("| %5llus ", mapping.mRemainingTimeMs / 1000); + OutputFormat("| %8llu ", mapping.mCounters.mTotal.m4To6Packets); + OutputFormat("| %12llu ", mapping.mCounters.mTotal.m4To6Bytes); + OutputFormat("| %8llu ", mapping.mCounters.mTotal.m6To4Packets); + OutputFormat("| %12llu ", mapping.mCounters.mTotal.m6To4Bytes); + + OutputLine("|"); + + OutputFormat("| %016s ", ""); + OutputFormat("| %68s ", "TCP"); + OutputFormat("| %8llu ", mapping.mCounters.mTcp.m4To6Packets); + OutputFormat("| %12llu ", mapping.mCounters.mTcp.m4To6Bytes); + OutputFormat("| %8llu ", mapping.mCounters.mTcp.m6To4Packets); + OutputFormat("| %12llu ", mapping.mCounters.mTcp.m6To4Bytes); + OutputLine("|"); + + OutputFormat("| %016s ", ""); + OutputFormat("| %68s ", "UDP"); + OutputFormat("| %8llu ", mapping.mCounters.mUdp.m4To6Packets); + OutputFormat("| %12llu ", mapping.mCounters.mUdp.m4To6Bytes); + OutputFormat("| %8llu ", mapping.mCounters.mUdp.m6To4Packets); + OutputFormat("| %12llu ", mapping.mCounters.mUdp.m6To4Bytes); + OutputLine("|"); + + OutputFormat("| %016s ", ""); + OutputFormat("| %68s ", "ICMP"); + OutputFormat("| %8llu ", mapping.mCounters.mIcmp.m4To6Packets); + OutputFormat("| %12llu ", mapping.mCounters.mIcmp.m4To6Bytes); + OutputFormat("| %8llu ", mapping.mCounters.mIcmp.m6To4Packets); + OutputFormat("| %12llu ", mapping.mCounters.mIcmp.m6To4Bytes); + OutputLine("|"); + } + } + /** + * @cli nat64 counters + * @code + * nat64 counters + * | | 4 to 6 | 6 to 4 | + * +---------------+-------------------------+-------------------------+ + * | Protocol | Pkts | Bytes | Pkts | Bytes | + * +---------------+----------+--------------+----------+--------------+ + * | Total | 11 | 704 | 11 | 704 | + * | TCP | 0 | 0 | 0 | 0 | + * | UDP | 0 | 0 | 0 | 0 | + * | ICMP | 11 | 704 | 11 | 704 | + * | Errors | Pkts | Pkts | + * +---------------+-------------------------+-------------------------+ + * | Total | 8 | 4 | + * | Illegal Pkt | 0 | 0 | + * | Unsup Proto | 0 | 0 | + * | No Mapping | 2 | 0 | + * Done + * @endcode + * @par + * Gets the NAT64 translator packet and error counters. + * @par + * Available when `OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE` is enabled. + * @sa otNat64GetCounters + * @sa otNat64GetErrorCounters + * + */ + else if (aArgs[0] == "counters") + { + static const char *const kNat64CounterTableHeader[] = { + "", + "4 to 6", + "6 to 4", + }; + static const uint8_t kNat64CounterTableHeaderColumns[] = {15, 25, 25}; + static const char *const kNat64CounterTableSubHeader[] = { + "Protocol", "Pkts", "Bytes", "Pkts", "Bytes", + }; + static const uint8_t kNat64CounterTableSubHeaderColumns[] = { + 15, 10, 14, 10, 14, + }; + static const char *const kNat64CounterTableErrorSubHeader[] = { + "Errors", + "Pkts", + "Pkts", + }; + static const uint8_t kNat64CounterTableErrorSubHeaderColumns[] = { + 15, + 25, + 25, + }; + static const char *const kNat64CounterErrorType[] = { + "Unknown", + "Illegal Pkt", + "Unsup Proto", + "No Mapping", + }; + + otNat64ProtocolCounters counters; + otNat64ErrorCounters errorCounters; + + OutputTableHeader(kNat64CounterTableHeader, kNat64CounterTableHeaderColumns); + OutputTableHeader(kNat64CounterTableSubHeader, kNat64CounterTableSubHeaderColumns); + + otNat64GetCounters(GetInstancePtr(), &counters); + otNat64GetErrorCounters(GetInstancePtr(), &errorCounters); + + OutputLine("| %13s | %8llu | %12llu | %8llu | %12llu |", "Total", counters.mTotal.m4To6Packets, + counters.mTotal.m4To6Bytes, counters.mTotal.m6To4Packets, counters.mTotal.m6To4Bytes); + OutputLine("| %13s | %8llu | %12llu | %8llu | %12llu |", "TCP", counters.mTcp.m4To6Packets, + counters.mTcp.m4To6Bytes, counters.mTcp.m6To4Packets, counters.mTcp.m6To4Bytes); + OutputLine("| %13s | %8llu | %12llu | %8llu | %12llu |", "UDP", counters.mUdp.m4To6Packets, + counters.mUdp.m4To6Bytes, counters.mUdp.m6To4Packets, counters.mUdp.m6To4Bytes); + OutputLine("| %13s | %8llu | %12llu | %8llu | %12llu |", "ICMP", counters.mIcmp.m4To6Packets, + counters.mIcmp.m4To6Bytes, counters.mIcmp.m6To4Packets, counters.mIcmp.m6To4Bytes); + + OutputTableHeader(kNat64CounterTableErrorSubHeader, kNat64CounterTableErrorSubHeaderColumns); + for (uint8_t i = 0; i < OT_NAT64_DROP_REASON_COUNT; i++) + { + OutputLine("| %13s | %23llu | %23llu |", kNat64CounterErrorType[i], errorCounters.mCount4To6[i], + errorCounters.mCount6To4[i]); + } + } + else + { + ExitNow(error = OT_ERROR_INVALID_COMMAND); + } + +exit: + return error; +} +#endif // OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE + #if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2) template <> otError Interpreter::Process(Arg aArgs[]) { @@ -4560,6 +4795,7 @@ template <> otError Interpreter::Process(Arg aArgs[]) otError error = OT_ERROR_NONE; otPingSenderConfig config; bool async = false; + bool nat64SynthesizedAddress; if (aArgs[0] == "stop") { @@ -4599,7 +4835,12 @@ template <> otError Interpreter::Process(Arg aArgs[]) aArgs += 2; } - SuccessOrExit(error = aArgs[0].ParseAsIp6Address(config.mDestination)); + SuccessOrExit(error = ParseToIp6Address(GetInstancePtr(), aArgs[0], config.mDestination, nat64SynthesizedAddress)); + if (nat64SynthesizedAddress) + { + OutputFormat("Pinging synthesized IPv6 address: "); + OutputIp6AddressLine(config.mDestination); + } if (!aArgs[1].IsEmpty()) { @@ -6468,6 +6709,9 @@ otError Interpreter::ProcessCommand(Arg aArgs[]) #endif CmdEntry("mode"), CmdEntry("multiradio"), +#if OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE + CmdEntry("nat64"), +#endif #if OPENTHREAD_FTD CmdEntry("neighbor"), #endif diff --git a/src/cli/cli.hpp b/src/cli/cli.hpp index 1a171952701..13972757a26 100644 --- a/src/cli/cli.hpp +++ b/src/cli/cli.hpp @@ -243,6 +243,27 @@ class Interpreter : public Output */ static const char *PreferenceToString(signed int aPreference); + /** + * This method parses the argument as an IP address. + * + * If the argument string is an IPv4 address, this method will try to synthersize an IPv6 address using preferred + * NAT64 prefix in the network data. + * + * @param[in] aInstance A pointer to openthread instance. + * @param[in] aArg The argument string to parse. + * @param[out] aAddress A reference to an `otIp6Address` to output the parsed IPv6 address. + * @param[out] aSynthesized Whether @p aAddress is synthesized from an IPv4 address. + * + * @retval OT_ERROR_NONE The argument was parsed successfully. + * @retval OT_ERROR_INVALID_ARGS The argument is empty or does not contain valid IP address. + * @retval OT_ERROR_INVALID_STATE No valid NAT64 prefix in the network data. + * + */ + static otError ParseToIp6Address(otInstance * aInstance, + const Arg & aArg, + otIp6Address &aAddress, + bool & aSynthesized); + protected: static Interpreter *sInterpreter; diff --git a/src/cli/cli_srp_server.cpp b/src/cli/cli_srp_server.cpp index e13b8eca68d..474b950ab8c 100644 --- a/src/cli/cli_srp_server.cpp +++ b/src/cli/cli_srp_server.cpp @@ -96,6 +96,47 @@ otError SrpServer::ProcessAddrMode(Arg aArgs[]) return error; } +#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE +otError SrpServer::ProcessAuto(Arg aArgs[]) +{ + otError error = OT_ERROR_NONE; + + /** + * @cli srp server auto + * @code + * srp server auto + * Disabled + * Done + * @endcode + * @par api_copy + * #otSrpServerIsAutoEnableMode + */ + if (aArgs[0].IsEmpty()) + { + OutputEnabledDisabledStatus(otSrpServerIsAutoEnableMode(GetInstancePtr())); + } + /** + * @cli srp server auto enable + * @code + * srp server auto enable + * Done + * @endcode + * @par api_copy + * #otSrpServerSetAutoEnableMode + */ + else + { + bool enable; + + SuccessOrExit(error = Interpreter::ParseEnableOrDisable(aArgs[0], enable)); + otSrpServerSetAutoEnableMode(GetInstancePtr(), enable); + } + +exit: + return error; +} +#endif + otError SrpServer::ProcessDomain(Arg aArgs[]) { otError error = OT_ERROR_NONE; diff --git a/src/cli/cli_srp_server.hpp b/src/cli/cli_srp_server.hpp index 363f5911863..1a29c9e95d8 100644 --- a/src/cli/cli_srp_server.hpp +++ b/src/cli/cli_srp_server.hpp @@ -82,6 +82,9 @@ class SrpServer : private OutputWrapper using Command = CommandEntry; otError ProcessAddrMode(Arg aArgs[]); +#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE + otError ProcessAuto(Arg aArgs[]); +#endif otError ProcessDomain(Arg aArgs[]); otError ProcessState(Arg aArgs[]); otError ProcessEnable(Arg aArgs[]); @@ -96,11 +99,19 @@ class SrpServer : private OutputWrapper void OutputHostAddresses(const otSrpServerHost *aHost); static constexpr Command sCommands[] = { - {"addrmode", &SrpServer::ProcessAddrMode}, {"disable", &SrpServer::ProcessDisable}, - {"domain", &SrpServer::ProcessDomain}, {"enable", &SrpServer::ProcessEnable}, - {"help", &SrpServer::ProcessHelp}, {"host", &SrpServer::ProcessHost}, - {"lease", &SrpServer::ProcessLease}, {"seqnum", &SrpServer::ProcessSeqNum}, - {"service", &SrpServer::ProcessService}, {"state", &SrpServer::ProcessState}, + {"addrmode", &SrpServer::ProcessAddrMode}, +#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE + {"auto", &SrpServer::ProcessAuto}, +#endif + {"disable", &SrpServer::ProcessDisable}, + {"domain", &SrpServer::ProcessDomain}, + {"enable", &SrpServer::ProcessEnable}, + {"help", &SrpServer::ProcessHelp}, + {"host", &SrpServer::ProcessHost}, + {"lease", &SrpServer::ProcessLease}, + {"seqnum", &SrpServer::ProcessSeqNum}, + {"service", &SrpServer::ProcessService}, + {"state", &SrpServer::ProcessState}, {"ttl", &SrpServer::ProcessTtl}, }; diff --git a/src/cli/cli_tcp.cpp b/src/cli/cli_tcp.cpp index 97076e9dfd9..79ece00d7a6 100644 --- a/src/cli/cli_tcp.cpp +++ b/src/cli/cli_tcp.cpp @@ -39,6 +39,7 @@ #include "cli_tcp.hpp" +#include #include #include "cli/cli.hpp" @@ -55,8 +56,9 @@ TcpExample::TcpExample(Output &aOutput) , mInitialized(false) , mEndpointConnected(false) , mSendBusy(false) + , mUseCircularSendBuffer(true) , mBenchmarkBytesTotal(0) - , mBenchmarkLinksLeft(0) + , mBenchmarkBytesUnsent(0) { } @@ -81,30 +83,59 @@ otError TcpExample::ProcessInit(Arg aArgs[]) if (aArgs[0].IsEmpty()) { - receiveBufferSize = sizeof(mReceiveBuffer); + mUseCircularSendBuffer = true; + receiveBufferSize = sizeof(mReceiveBufferBytes); } else { - uint32_t windowSize; + if (aArgs[0] == "circular") + { + mUseCircularSendBuffer = true; + } + else if (aArgs[0] == "linked") + { + mUseCircularSendBuffer = false; + } + else + { + ExitNow(error = OT_ERROR_INVALID_ARGS); + } - SuccessOrExit(error = aArgs[0].ParseAsUint32(windowSize)); - VerifyOrExit(aArgs[1].IsEmpty(), error = OT_ERROR_INVALID_ARGS); + if (aArgs[1].IsEmpty()) + { + receiveBufferSize = sizeof(mReceiveBufferBytes); + } + else + { + uint32_t windowSize; - receiveBufferSize = windowSize + ((windowSize + 7) >> 3); - VerifyOrExit(receiveBufferSize <= sizeof(mReceiveBuffer) && receiveBufferSize != 0, - error = OT_ERROR_INVALID_ARGS); + SuccessOrExit(error = aArgs[1].ParseAsUint32(windowSize)); + + receiveBufferSize = windowSize + ((windowSize + 7) >> 3); + VerifyOrExit(receiveBufferSize <= sizeof(mReceiveBufferBytes) && receiveBufferSize != 0, + error = OT_ERROR_INVALID_ARGS); + } } + otTcpCircularSendBufferInitialize(&mSendBuffer, mSendBufferBytes, sizeof(mSendBufferBytes)); + { otTcpEndpointInitializeArgs endpointArgs; memset(&endpointArgs, 0x00, sizeof(endpointArgs)); - endpointArgs.mEstablishedCallback = HandleTcpEstablishedCallback; - endpointArgs.mSendDoneCallback = HandleTcpSendDoneCallback; + endpointArgs.mEstablishedCallback = HandleTcpEstablishedCallback; + if (mUseCircularSendBuffer) + { + endpointArgs.mForwardProgressCallback = HandleTcpForwardProgressCallback; + } + else + { + endpointArgs.mSendDoneCallback = HandleTcpSendDoneCallback; + } endpointArgs.mReceiveAvailableCallback = HandleTcpReceiveAvailableCallback; endpointArgs.mDisconnectedCallback = HandleTcpDisconnectedCallback; endpointArgs.mContext = this; - endpointArgs.mReceiveBuffer = mReceiveBuffer; + endpointArgs.mReceiveBuffer = mReceiveBufferBytes; endpointArgs.mReceiveBufferSize = receiveBufferSize; SuccessOrExit(error = otTcpEndpointInitialize(GetInstancePtr(), &mEndpoint, &endpointArgs)); @@ -136,6 +167,7 @@ otError TcpExample::ProcessDeinit(Arg aArgs[]) { otError error = OT_ERROR_NONE; otError endpointError; + otError bufferError; otError listenerError; VerifyOrExit(aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS); @@ -144,10 +176,14 @@ otError TcpExample::ProcessDeinit(Arg aArgs[]) endpointError = otTcpEndpointDeinitialize(&mEndpoint); mSendBusy = false; + otTcpCircularSendBufferForceDiscardAll(&mSendBuffer); + bufferError = otTcpCircularSendBufferDeinitialize(&mSendBuffer); + listenerError = otTcpListenerDeinitialize(&mListener); mInitialized = false; SuccessOrExit(error = endpointError); + SuccessOrExit(error = bufferError); SuccessOrExit(error = listenerError); exit: @@ -175,10 +211,18 @@ otError TcpExample::ProcessConnect(Arg aArgs[]) { otError error; otSockAddr sockaddr; + bool nat64SynthesizedAddress; VerifyOrExit(mInitialized, error = OT_ERROR_INVALID_STATE); - SuccessOrExit(error = aArgs[0].ParseAsIp6Address(sockaddr.mAddress)); + SuccessOrExit( + error = Interpreter::ParseToIp6Address(GetInstancePtr(), aArgs[0], sockaddr.mAddress, nat64SynthesizedAddress)); + if (nat64SynthesizedAddress) + { + OutputFormat("Connecting to synthesized IPv6 address: "); + OutputIp6AddressLine(sockaddr.mAddress); + } + SuccessOrExit(error = aArgs[1].ParseAsUint16(sockaddr.mPort)); VerifyOrExit(aArgs[2].IsEmpty(), error = OT_ERROR_INVALID_ARGS); @@ -194,18 +238,28 @@ otError TcpExample::ProcessSend(Arg aArgs[]) otError error; VerifyOrExit(mInitialized, error = OT_ERROR_INVALID_STATE); - VerifyOrExit(!mSendBusy, error = OT_ERROR_BUSY); VerifyOrExit(mBenchmarkBytesTotal == 0, error = OT_ERROR_BUSY); - - mSendLink.mNext = nullptr; - mSendLink.mData = mSendBuffer; VerifyOrExit(!aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS); - mSendLink.mLength = OT_MIN(aArgs[0].GetLength(), sizeof(mSendBuffer)); - memcpy(mSendBuffer, aArgs[0].GetCString(), mSendLink.mLength); VerifyOrExit(aArgs[1].IsEmpty(), error = OT_ERROR_INVALID_ARGS); - SuccessOrExit(error = otTcpSendByReference(&mEndpoint, &mSendLink, 0)); - mSendBusy = true; + if (mUseCircularSendBuffer) + { + size_t written; + SuccessOrExit(error = otTcpCircularSendBufferWrite(&mEndpoint, &mSendBuffer, aArgs[0].GetCString(), + aArgs[0].GetLength(), &written, 0)); + } + else + { + VerifyOrExit(!mSendBusy, error = OT_ERROR_BUSY); + + mSendLink.mNext = nullptr; + mSendLink.mData = mSendBufferBytes; + mSendLink.mLength = OT_MIN(aArgs[0].GetLength(), sizeof(mSendBufferBytes)); + memcpy(mSendBufferBytes, aArgs[0].GetCString(), mSendLink.mLength); + + SuccessOrExit(error = otTcpSendByReference(&mEndpoint, &mSendLink, 0)); + mSendBusy = true; + } exit: return error; @@ -213,8 +267,7 @@ otError TcpExample::ProcessSend(Arg aArgs[]) otError TcpExample::ProcessBenchmark(Arg aArgs[]) { - otError error = OT_ERROR_NONE; - uint32_t toSendOut; + otError error = OT_ERROR_NONE; VerifyOrExit(!mSendBusy, error = OT_ERROR_BUSY); VerifyOrExit(mBenchmarkBytesTotal == 0, error = OT_ERROR_BUSY); @@ -230,30 +283,37 @@ otError TcpExample::ProcessBenchmark(Arg aArgs[]) } VerifyOrExit(aArgs[1].IsEmpty(), error = OT_ERROR_INVALID_ARGS); - memset(mSendBuffer, 'a', sizeof(mSendBuffer)); + mBenchmarkStart = TimerMilli::GetNow(); + mBenchmarkBytesUnsent = mBenchmarkBytesTotal; - mBenchmarkLinksLeft = (mBenchmarkBytesTotal + sizeof(mSendBuffer) - 1) / sizeof(mSendBuffer); - toSendOut = OT_MIN(OT_ARRAY_LENGTH(mBenchmarkLinks), mBenchmarkLinksLeft); - mBenchmarkStart = TimerMilli::GetNow(); - for (uint32_t i = 0; i != toSendOut; i++) + if (mUseCircularSendBuffer) { - mBenchmarkLinks[i].mNext = nullptr; - mBenchmarkLinks[i].mData = mSendBuffer; - mBenchmarkLinks[i].mLength = sizeof(mSendBuffer); - if (i == 0 && mBenchmarkBytesTotal % sizeof(mSendBuffer) != 0) + SuccessOrExit(error = ContinueBenchmarkCircularSend()); + } + else + { + uint32_t benchmarkLinksLeft = (mBenchmarkBytesTotal + sizeof(mSendBufferBytes) - 1) / sizeof(mSendBufferBytes); + uint32_t toSendOut = OT_MIN(OT_ARRAY_LENGTH(mBenchmarkLinks), benchmarkLinksLeft); + + /* We could also point the linked buffers directly to sBenchmarkData. */ + memset(mSendBufferBytes, 'a', sizeof(mSendBufferBytes)); + + for (uint32_t i = 0; i != toSendOut; i++) { - mBenchmarkLinks[i].mLength = mBenchmarkBytesTotal % sizeof(mSendBuffer); + mBenchmarkLinks[i].mNext = nullptr; + mBenchmarkLinks[i].mData = mSendBufferBytes; + mBenchmarkLinks[i].mLength = sizeof(mSendBufferBytes); + if (i == 0 && mBenchmarkBytesTotal % sizeof(mSendBufferBytes) != 0) + { + mBenchmarkLinks[i].mLength = mBenchmarkBytesTotal % sizeof(mSendBufferBytes); + } + error = otTcpSendByReference(&mEndpoint, &mBenchmarkLinks[i], + i == toSendOut - 1 ? 0 : OT_TCP_SEND_MORE_TO_COME); + VerifyOrExit(error == OT_ERROR_NONE, mBenchmarkBytesTotal = 0); } - SuccessOrExit(error = otTcpSendByReference(&mEndpoint, &mBenchmarkLinks[i], - i == toSendOut - 1 ? 0 : OT_TCP_SEND_MORE_TO_COME)); } exit: - if (error != OT_ERROR_NONE) - { - mBenchmarkBytesTotal = 0; - mBenchmarkLinksLeft = 0; - } return error; } @@ -341,6 +401,12 @@ void TcpExample::HandleTcpSendDoneCallback(otTcpEndpoint *aEndpoint, otLinkedBuf static_cast(otTcpEndpointGetContext(aEndpoint))->HandleTcpSendDone(aEndpoint, aData); } +void TcpExample::HandleTcpForwardProgressCallback(otTcpEndpoint *aEndpoint, size_t aInSendBuffer, size_t aBacklog) +{ + static_cast(otTcpEndpointGetContext(aEndpoint)) + ->HandleTcpForwardProgress(aEndpoint, aInSendBuffer, aBacklog); +} + void TcpExample::HandleTcpReceiveAvailableCallback(otTcpEndpoint *aEndpoint, size_t aBytesAvailable, bool aEndOfStream, @@ -379,6 +445,7 @@ void TcpExample::HandleTcpEstablished(otTcpEndpoint *aEndpoint) void TcpExample::HandleTcpSendDone(otTcpEndpoint *aEndpoint, otLinkedBuffer *aData) { OT_UNUSED_VARIABLE(aEndpoint); + OT_ASSERT(!mUseCircularSendBuffer); // this callback is not used when using the circular send buffer if (mBenchmarkBytesTotal == 0) { @@ -393,25 +460,44 @@ void TcpExample::HandleTcpSendDone(otTcpEndpoint *aEndpoint, otLinkedBuffer *aDa else { OT_ASSERT(aData != &mSendLink); - mBenchmarkLinksLeft--; - if (mBenchmarkLinksLeft >= OT_ARRAY_LENGTH(mBenchmarkLinks)) + OT_ASSERT(mBenchmarkBytesUnsent >= aData->mLength); + mBenchmarkBytesUnsent -= aData->mLength; // could be less than sizeof(mSendBufferBytes) for the first link + if (mBenchmarkBytesUnsent >= OT_ARRAY_LENGTH(mBenchmarkLinks) * sizeof(mSendBufferBytes)) { - aData->mLength = sizeof(mSendBuffer); + aData->mLength = sizeof(mSendBufferBytes); if (otTcpSendByReference(&mEndpoint, aData, 0) != OT_ERROR_NONE) { OutputLine("TCP Benchmark Failed"); mBenchmarkBytesTotal = 0; } } - else if (mBenchmarkLinksLeft == 0) + else if (mBenchmarkBytesUnsent == 0) { - uint32_t milliseconds = TimerMilli::GetNow() - mBenchmarkStart; - uint32_t thousandTimesGoodput = (1000 * (mBenchmarkBytesTotal << 3) + (milliseconds >> 1)) / milliseconds; + CompleteBenchmark(); + } + } +} + +void TcpExample::HandleTcpForwardProgress(otTcpEndpoint *aEndpoint, size_t aInSendBuffer, size_t aBacklog) +{ + OT_UNUSED_VARIABLE(aEndpoint); + OT_UNUSED_VARIABLE(aBacklog); + OT_ASSERT(mUseCircularSendBuffer); // this callback is only used when using the circular send buffer + + otTcpCircularSendBufferHandleForwardProgress(&mSendBuffer, aInSendBuffer); - OutputLine("TCP Benchmark Complete: Transferred %u bytes in %u milliseconds", - static_cast(mBenchmarkBytesTotal), static_cast(milliseconds)); - OutputLine("TCP Goodput: %u.%03u kb/s", thousandTimesGoodput / 1000, thousandTimesGoodput % 1000); - mBenchmarkBytesTotal = 0; + /* Handle case where we're in a benchmark. */ + if (mBenchmarkBytesTotal != 0) + { + if (mBenchmarkBytesUnsent != 0) + { + /* Continue sending out data if there's data we haven't sent. */ + IgnoreError(ContinueBenchmarkCircularSend()); + } + else if (aInSendBuffer == 0) + { + /* Handle case where all data is sent out and the send buffer has drained. */ + CompleteBenchmark(); } } } @@ -432,7 +518,7 @@ void TcpExample::HandleTcpReceiveAvailable(otTcpEndpoint *aEndpoint, IgnoreError(otTcpReceiveByReference(aEndpoint, &data)); for (; data != nullptr; data = data->mNext) { - OutputLine("TCP: Received %u bytes: %.*s", static_cast(data->mLength), data->mLength, + OutputLine("TCP: Received %u bytes: %.*s", data->mLength, data->mLength, reinterpret_cast(data->mData)); totalReceived += data->mLength; } @@ -473,17 +559,18 @@ void TcpExample::HandleTcpDisconnected(otTcpEndpoint *aEndpoint, otTcpDisconnect mSendBusy = false; // Mark the benchmark as inactive if the connection was disconnected. - if (mBenchmarkBytesTotal != 0) - { - mBenchmarkBytesTotal = 0; - mBenchmarkLinksLeft = 0; - } + mBenchmarkBytesTotal = 0; + mBenchmarkBytesUnsent = 0; + + otTcpCircularSendBufferForceDiscardAll(&mSendBuffer); } otTcpIncomingConnectionAction TcpExample::HandleTcpAcceptReady(otTcpListener * aListener, const otSockAddr *aPeer, otTcpEndpoint ** aAcceptInto) { + otTcpIncomingConnectionAction action; + OT_UNUSED_VARIABLE(aListener); if (mEndpointConnected) @@ -492,11 +579,14 @@ otTcpIncomingConnectionAction TcpExample::HandleTcpAcceptReady(otTcpListener * OutputSockAddr(*aPeer); OutputLine(" (active socket is busy)"); - return OT_TCP_INCOMING_CONNECTION_ACTION_DEFER; + ExitNow(action = OT_TCP_INCOMING_CONNECTION_ACTION_DEFER); } *aAcceptInto = &mEndpoint; - return OT_TCP_INCOMING_CONNECTION_ACTION_ACCEPT; + action = OT_TCP_INCOMING_CONNECTION_ACTION_ACCEPT; + +exit: + return action; } void TcpExample::HandleTcpAcceptDone(otTcpListener *aListener, otTcpEndpoint *aEndpoint, const otSockAddr *aPeer) @@ -508,6 +598,45 @@ void TcpExample::HandleTcpAcceptDone(otTcpListener *aListener, otTcpEndpoint *aE OutputSockAddrLine(*aPeer); } +otError TcpExample::ContinueBenchmarkCircularSend(void) +{ + otError error = OT_ERROR_NONE; + size_t freeSpace; + + while (mBenchmarkBytesUnsent != 0 && (freeSpace = otTcpCircularSendBufferGetFreeSpace(&mSendBuffer)) != 0) + { + size_t toSendThisIteration = OT_MIN(mBenchmarkBytesUnsent, sBenchmarkDataLength); + uint32_t flag = (toSendThisIteration < freeSpace && toSendThisIteration < mBenchmarkBytesUnsent) + ? OT_TCP_CIRCULAR_SEND_BUFFER_WRITE_MORE_TO_COME + : 0; + size_t written; + + SuccessOrExit(error = otTcpCircularSendBufferWrite(&mEndpoint, &mSendBuffer, sBenchmarkData, + toSendThisIteration, &written, flag)); + mBenchmarkBytesUnsent -= written; + } + +exit: + if (error != OT_ERROR_NONE) + { + OutputLine("TCP Benchmark Failed"); + mBenchmarkBytesTotal = 0; + mBenchmarkBytesUnsent = 0; + } + + return error; +} + +void TcpExample::CompleteBenchmark(void) +{ + uint32_t milliseconds = TimerMilli::GetNow() - mBenchmarkStart; + uint32_t thousandTimesGoodput = (1000 * (mBenchmarkBytesTotal << 3) + (milliseconds >> 1)) / milliseconds; + + OutputLine("TCP Benchmark Complete: Transferred %u bytes in %u milliseconds", mBenchmarkBytesTotal, milliseconds); + OutputLine("TCP Goodput: %u.%03u kb/s", thousandTimesGoodput / 1000, thousandTimesGoodput % 1000); + mBenchmarkBytesTotal = 0; +} + } // namespace Cli } // namespace ot diff --git a/src/cli/cli_tcp.hpp b/src/cli/cli_tcp.hpp index 4bebc963222..d450a37d0cc 100644 --- a/src/cli/cli_tcp.hpp +++ b/src/cli/cli_tcp.hpp @@ -37,6 +37,7 @@ #include "openthread-core-config.h" #include +#include #include "cli/cli_config.h" #include "cli/cli_output.hpp" @@ -85,8 +86,12 @@ class TcpExample : private OutputWrapper otError ProcessListen(Arg aArgs[]); otError ProcessStopListening(Arg aArgs[]); + otError ContinueBenchmarkCircularSend(void); + void CompleteBenchmark(void); + static void HandleTcpEstablishedCallback(otTcpEndpoint *aEndpoint); static void HandleTcpSendDoneCallback(otTcpEndpoint *aEndpoint, otLinkedBuffer *aData); + static void HandleTcpForwardProgressCallback(otTcpEndpoint *aEndpoint, size_t aInSendBuffer, size_t aBacklog); static void HandleTcpReceiveAvailableCallback(otTcpEndpoint *aEndpoint, size_t aBytesAvailable, bool aEndOfStream, @@ -99,13 +104,14 @@ class TcpExample : private OutputWrapper otTcpEndpoint * aEndpoint, const otSockAddr *aPeer); - void HandleTcpEstablished(otTcpEndpoint *aEndpoint); - void HandleTcpSendDone(otTcpEndpoint *aEndpoint, otLinkedBuffer *aData); - void HandleTcpReceiveAvailable(otTcpEndpoint *aEndpoint, - size_t aBytesAvailable, - bool aEndOfStream, - size_t aBytesRemaining); - void HandleTcpDisconnected(otTcpEndpoint *aEndpoint, otTcpDisconnectedReason aReason); + void HandleTcpEstablished(otTcpEndpoint *aEndpoint); + void HandleTcpSendDone(otTcpEndpoint *aEndpoint, otLinkedBuffer *aData); + void HandleTcpForwardProgress(otTcpEndpoint *aEndpoint, size_t aInSendBuffer, size_t aBacklog); + void HandleTcpReceiveAvailable(otTcpEndpoint *aEndpoint, + size_t aBytesAvailable, + bool aEndOfStream, + size_t aBytesRemaining); + void HandleTcpDisconnected(otTcpEndpoint *aEndpoint, otTcpDisconnectedReason aReason); otTcpIncomingConnectionAction HandleTcpAcceptReady(otTcpListener * aListener, const otSockAddr *aPeer, otTcpEndpoint ** aAcceptInto); @@ -133,15 +139,21 @@ class TcpExample : private OutputWrapper bool mInitialized; bool mEndpointConnected; bool mSendBusy; + bool mUseCircularSendBuffer; + + otTcpCircularSendBuffer mSendBuffer; + otLinkedBuffer mSendLink; + uint8_t mSendBufferBytes[OPENTHREAD_CONFIG_CLI_TCP_RECEIVE_BUFFER_SIZE]; + uint8_t mReceiveBufferBytes[OPENTHREAD_CONFIG_CLI_TCP_RECEIVE_BUFFER_SIZE]; - otLinkedBuffer mSendLink; - uint8_t mSendBuffer[OPENTHREAD_CONFIG_CLI_MAX_LINE_LENGTH]; - uint8_t mReceiveBuffer[OPENTHREAD_CONFIG_CLI_TCP_RECEIVE_BUFFER_SIZE]; + otLinkedBuffer + mBenchmarkLinks[(sizeof(mReceiveBufferBytes) + sizeof(mSendBufferBytes) - 1) / sizeof(mSendBufferBytes)]; + uint32_t mBenchmarkBytesTotal; + uint32_t mBenchmarkBytesUnsent; + TimeMilli mBenchmarkStart; - otLinkedBuffer mBenchmarkLinks[(sizeof(mReceiveBuffer) + sizeof(mSendBuffer) - 1) / sizeof(mSendBuffer)]; - uint32_t mBenchmarkBytesTotal; - uint32_t mBenchmarkLinksLeft; - TimeMilli mBenchmarkStart; + static constexpr const char * sBenchmarkData = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; + static constexpr const size_t sBenchmarkDataLength = 52; }; } // namespace Cli diff --git a/src/cli/cli_udp.cpp b/src/cli/cli_udp.cpp index 62ad05790ad..22b39cc1775 100644 --- a/src/cli/cli_udp.cpp +++ b/src/cli/cli_udp.cpp @@ -34,6 +34,7 @@ #include "cli_udp.hpp" #include +#include #include #include "cli/cli.hpp" @@ -94,8 +95,16 @@ otError UdpExample::ProcessConnect(Arg aArgs[]) { otError error; otSockAddr sockaddr; + bool nat64SynthesizedAddress; + + SuccessOrExit( + error = Interpreter::ParseToIp6Address(GetInstancePtr(), aArgs[0], sockaddr.mAddress, nat64SynthesizedAddress)); + if (nat64SynthesizedAddress) + { + OutputFormat("Connecting to synthesized IPv6 address: "); + OutputIp6AddressLine(sockaddr.mAddress); + } - SuccessOrExit(error = aArgs[0].ParseAsIp6Address(sockaddr.mAddress)); SuccessOrExit(error = aArgs[1].ParseAsUint16(sockaddr.mPort)); VerifyOrExit(aArgs[2].IsEmpty(), error = OT_ERROR_INVALID_ARGS); @@ -143,7 +152,16 @@ otError UdpExample::ProcessSend(Arg aArgs[]) if (!aArgs[2].IsEmpty()) { - SuccessOrExit(error = aArgs[0].ParseAsIp6Address(messageInfo.mPeerAddr)); + bool nat64SynthesizedAddress; + + SuccessOrExit(error = Interpreter::ParseToIp6Address(GetInstancePtr(), aArgs[0], messageInfo.mPeerAddr, + nat64SynthesizedAddress)); + if (nat64SynthesizedAddress) + { + OutputFormat("Sending to synthesized IPv6 address: "); + OutputIp6AddressLine(messageInfo.mPeerAddr); + } + SuccessOrExit(error = aArgs[1].ParseAsUint16(messageInfo.mPeerPort)); aArgs += 2; } diff --git a/src/core/BUILD.gn b/src/core/BUILD.gn index 17a5836bd61..f49d659fe4f 100644 --- a/src/core/BUILD.gn +++ b/src/core/BUILD.gn @@ -356,6 +356,7 @@ openthread_core_files = [ "api/srp_server_api.cpp", "api/tasklet_api.cpp", "api/tcp_api.cpp", + "api/tcp_ext_api.cpp", "api/thread_api.cpp", "api/thread_ftd_api.cpp", "api/trel_api.cpp", @@ -590,6 +591,8 @@ openthread_core_files = [ "net/srp_server.hpp", "net/tcp6.cpp", "net/tcp6.hpp", + "net/tcp6_ext.cpp", + "net/tcp6_ext.hpp", "net/udp6.cpp", "net/udp6.hpp", "radio/max_power_table.hpp", diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index d95baa6c88d..657ac7d8b12 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -79,6 +79,7 @@ set(COMMON_SOURCES api/srp_server_api.cpp api/tasklet_api.cpp api/tcp_api.cpp + api/tcp_ext_api.cpp api/thread_api.cpp api/thread_ftd_api.cpp api/trel_api.cpp @@ -183,6 +184,7 @@ set(COMMON_SOURCES net/srp_client.cpp net/srp_server.cpp net/tcp6.cpp + net/tcp6_ext.cpp net/udp6.cpp radio/radio.cpp radio/radio_callbacks.cpp diff --git a/src/core/Makefile.am b/src/core/Makefile.am index 63576b7f940..d417fe75e53 100644 --- a/src/core/Makefile.am +++ b/src/core/Makefile.am @@ -169,6 +169,7 @@ SOURCES_COMMON = \ api/srp_server_api.cpp \ api/tasklet_api.cpp \ api/tcp_api.cpp \ + api/tcp_ext_api.cpp \ api/thread_api.cpp \ api/thread_ftd_api.cpp \ api/trel_api.cpp \ @@ -273,6 +274,7 @@ SOURCES_COMMON = \ net/srp_client.cpp \ net/srp_server.cpp \ net/tcp6.cpp \ + net/tcp6_ext.cpp \ net/udp6.cpp \ radio/radio.cpp \ radio/radio_callbacks.cpp \ @@ -590,6 +592,7 @@ HEADERS_COMMON = \ net/srp_client.hpp \ net/srp_server.hpp \ net/tcp6.hpp \ + net/tcp6_ext.hpp \ net/udp6.hpp \ openthread-core-config.h \ radio/max_power_table.hpp \ diff --git a/src/core/api/ip6_api.cpp b/src/core/api/ip6_api.cpp index 2da28f00446..454132b4d15 100644 --- a/src/core/api/ip6_api.cpp +++ b/src/core/api/ip6_api.cpp @@ -39,6 +39,7 @@ #include "common/locator_getters.hpp" #include "net/ip4_types.hpp" #include "net/ip6_headers.hpp" +#include "thread/network_data_leader.hpp" #include "utils/slaac_address.hpp" using namespace ot; diff --git a/src/core/api/nat64_api.cpp b/src/core/api/nat64_api.cpp index 61d66f2cb0d..5e16c284d1a 100644 --- a/src/core/api/nat64_api.cpp +++ b/src/core/api/nat64_api.cpp @@ -66,6 +66,38 @@ void otNat64SetReceiveIp4Callback(otInstance *aInstance, otNat64ReceiveIp4Callba { AsCoreType(aInstance).Get().SetNat64ReceiveIp4DatagramCallback(aCallback, aContext); } + +void otNat64InitAddressMappingIterator(otInstance *aInstance, otNat64AddressMappingIterator *aIterator) +{ + AssertPointerIsNotNull(aIterator); + + AsCoreType(aInstance).Get().InitAddressMappingIterator(*aIterator); +} + +otError otNat64GetNextAddressMapping(otInstance * aInstance, + otNat64AddressMappingIterator *aIterator, + otNat64AddressMapping * aMapping) +{ + AssertPointerIsNotNull(aIterator); + AssertPointerIsNotNull(aMapping); + + return AsCoreType(aInstance).Get().GetNextAddressMapping(*aIterator, *aMapping); +} + +void otNat64GetCounters(otInstance *aInstance, otNat64ProtocolCounters *aCounters) +{ + AsCoreType(aInstance).Get().GetCounters(AsCoreType(aCounters)); +} + +void otNat64GetErrorCounters(otInstance *aInstance, otNat64ErrorCounters *aCounters) +{ + AsCoreType(aInstance).Get().GetErrorCounters(AsCoreType(aCounters)); +} + +otError otNat64GetCidr(otInstance *aInstance, otIp4Cidr *aCidr) +{ + return AsCoreType(aInstance).Get().GetIp4Cidr(AsCoreType(aCidr)); +} #endif // OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE bool otIp4IsAddressEqual(const otIp4Address *aFirst, const otIp4Address *aSecond) @@ -77,3 +109,36 @@ void otIp4ExtractFromIp6Address(uint8_t aPrefixLength, const otIp6Address *aIp6A { AsCoreType(aIp4Address).ExtractFromIp6Address(aPrefixLength, AsCoreType(aIp6Address)); } + +otError otIp4AddressFromString(const char *aString, otIp4Address *aAddress) +{ + AssertPointerIsNotNull(aString); + return AsCoreType(aAddress).FromString(aString); +} + +otError otNat64SynthersizeIp6Address(otInstance *aInstance, const otIp4Address *aIp4Address, otIp6Address *aIp6Address) +{ + otError err = OT_ERROR_NONE; + NetworkData::ExternalRouteConfig nat64Prefix; + + VerifyOrExit(AsCoreType(aInstance).Get().GetPreferredNat64Prefix(nat64Prefix) == OT_ERROR_NONE, + err = OT_ERROR_INVALID_STATE); + AsCoreType(aIp6Address).SynthesizeFromIp4Address(nat64Prefix.GetPrefix(), AsCoreType(aIp4Address)); + +exit: + return err; +} + +void otIp4AddressToString(const otIp4Address *aAddress, char *aBuffer, uint16_t aSize) +{ + AssertPointerIsNotNull(aBuffer); + + AsCoreType(aAddress).ToString(aBuffer, aSize); +} + +void otIp4CidrToString(const otIp4Cidr *aCidr, char *aBuffer, uint16_t aSize) +{ + AssertPointerIsNotNull(aBuffer); + + AsCoreType(aCidr).ToString(aBuffer, aSize); +} diff --git a/src/core/api/srp_server_api.cpp b/src/core/api/srp_server_api.cpp index f9d4367e41d..488164a256c 100644 --- a/src/core/api/srp_server_api.cpp +++ b/src/core/api/srp_server_api.cpp @@ -87,6 +87,18 @@ void otSrpServerSetEnabled(otInstance *aInstance, bool aEnabled) AsCoreType(aInstance).Get().SetEnabled(aEnabled); } +#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE +void otSrpServerSetAutoEnableMode(otInstance *aInstance, bool aEnabled) +{ + AsCoreType(aInstance).Get().SetAutoEnableMode(aEnabled); +} + +bool otSrpServerIsAutoEnableMode(otInstance *aInstance) +{ + return AsCoreType(aInstance).Get().IsAutoEnableMode(); +} +#endif + void otSrpServerGetTtlConfig(otInstance *aInstance, otSrpServerTtlConfig *aTtlConfig) { AsCoreType(aInstance).Get().GetTtlConfig(AsCoreType(aTtlConfig)); diff --git a/src/core/api/tcp_ext_api.cpp b/src/core/api/tcp_ext_api.cpp new file mode 100644 index 00000000000..2e98b7f3992 --- /dev/null +++ b/src/core/api/tcp_ext_api.cpp @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2022, The OpenThread Authors. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file + * This file implements extensions to the OpenThread TCP API. + */ + +#include "openthread-core-config.h" + +#if OPENTHREAD_CONFIG_TCP_ENABLE + +#include + +#include "common/as_core_type.hpp" +#include "common/locator_getters.hpp" +#include "net/tcp6_ext.hpp" + +using namespace ot; + +void otTcpCircularSendBufferInitialize(otTcpCircularSendBuffer *aSendBuffer, void *aDataBuffer, size_t aCapacity) +{ + AsCoreType(aSendBuffer).Initialize(aDataBuffer, aCapacity); +} + +otError otTcpCircularSendBufferWrite(otTcpEndpoint * aEndpoint, + otTcpCircularSendBuffer *aSendBuffer, + const void * aData, + size_t aLength, + size_t * aWritten, + uint32_t aFlags) +{ + AssertPointerIsNotNull(aWritten); + return AsCoreType(aSendBuffer).Write(AsCoreType(aEndpoint), aData, aLength, *aWritten, aFlags); +} + +void otTcpCircularSendBufferHandleForwardProgress(otTcpCircularSendBuffer *aSendBuffer, size_t aInSendBuffer) +{ + AsCoreType(aSendBuffer).HandleForwardProgress(aInSendBuffer); +} + +size_t otTcpCircularSendBufferGetFreeSpace(const otTcpCircularSendBuffer *aSendBuffer) +{ + return AsCoreType(aSendBuffer).GetFreeSpace(); +} + +void otTcpCircularSendBufferForceDiscardAll(otTcpCircularSendBuffer *aSendBuffer) +{ + AsCoreType(aSendBuffer).ForceDiscardAll(); +} + +otError otTcpCircularSendBufferDeinitialize(otTcpCircularSendBuffer *aSendBuffer) +{ + return AsCoreType(aSendBuffer).Deinitialize(); +} + +#endif // OPENTHREAD_CONFIG_TCP_ENABLE diff --git a/src/core/border_router/infra_if.cpp b/src/core/border_router/infra_if.cpp index 2b754cbde01..c1875d1d23f 100644 --- a/src/core/border_router/infra_if.cpp +++ b/src/core/border_router/infra_if.cpp @@ -128,7 +128,7 @@ void InfraIf::DiscoverNat64PrefixDone(uint32_t aIfIndex, const Ip6::Prefix &aPre VerifyOrExit(aIfIndex == mIfIndex, error = kErrorInvalidArgs); #if OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE - Get().UpdateInfraIfNat64Prefix(aPrefix); + Get().HandleDiscoverNat64PrefixDone(aPrefix); #endif exit: diff --git a/src/core/border_router/routing_manager.cpp b/src/core/border_router/routing_manager.cpp index 642b15f54bd..827af13ce18 100644 --- a/src/core/border_router/routing_manager.cpp +++ b/src/core/border_router/routing_manager.cpp @@ -72,21 +72,15 @@ RoutingManager::RoutingManager(Instance &aInstance) , mLocalOnLinkPrefix(aInstance) , mDiscoveredPrefixTable(aInstance) #if OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE - , mInfraIfNat64PrefixStaleTimer(aInstance, HandleInfraIfNat64PrefixStaleTimer) + , mNat64PrefixManager(aInstance) #endif + , mRsSender(aInstance) , mDiscoveredPrefixStaleTimer(aInstance, HandleDiscoveredPrefixStaleTimer) - , mRouterSolicitTimer(aInstance, HandleRouterSolicitTimer) - , mRouterSolicitCount(0) , mRoutingPolicyTimer(aInstance, HandleRoutingPolicyTimer) { mFavoredDiscoveredOnLinkPrefix.Clear(); mBrUlaPrefix.Clear(); -#if OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE - mInfraIfNat64Prefix.Clear(); - mLocalNat64Prefix.Clear(); - mPublishedNat64Prefix.Clear(); -#endif } Error RoutingManager::Init(uint32_t aInfraIfIndex, bool aInfraIfIsRunning) @@ -98,7 +92,7 @@ Error RoutingManager::Init(uint32_t aInfraIfIndex, bool aInfraIfIsRunning) SuccessOrExit(error = LoadOrGenerateRandomBrUlaPrefix()); mLocalOmrPrefix.GenerateFrom(mBrUlaPrefix); #if OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE - GenerateNat64Prefix(); + mNat64PrefixManager.GenerateLocalPrefix(mBrUlaPrefix); #endif mLocalOnLinkPrefix.Generate(); @@ -181,7 +175,7 @@ Error RoutingManager::GetNat64Prefix(Ip6::Prefix &aPrefix) Error error = kErrorNone; VerifyOrExit(IsInitialized(), error = kErrorInvalidState); - aPrefix = mLocalNat64Prefix; + aPrefix = mNat64PrefixManager.GetLocalPrefix(); exit: return error; @@ -192,9 +186,7 @@ Error RoutingManager::GetFavoredNat64Prefix(Ip6::Prefix &aPrefix, RoutePreferenc Error error = kErrorNone; VerifyOrExit(IsInitialized(), error = kErrorInvalidState); - aPrefix = mInfraIfNat64Prefix.IsValidNat64() ? mInfraIfNat64Prefix : mLocalNat64Prefix; - aRoutePreference = - mInfraIfNat64Prefix.IsValidNat64() ? NetworkData::kRoutePreferenceMedium : NetworkData::kRoutePreferenceLow; + aPrefix = mNat64PrefixManager.GetFavoredPrefix(aRoutePreference); exit: return error; @@ -234,47 +226,6 @@ Error RoutingManager::LoadOrGenerateRandomBrUlaPrefix(void) return error; } -#if OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE -void RoutingManager::DiscoverInfraIfNat64Prefix(void) -{ - Error error = kErrorNone; - - VerifyOrExit(IsInitialized() && mInfraIf.IsRunning(), error = kErrorInvalidState); - - LogInfo("Discovering infrastructure NAT64 prefix on %s", mInfraIf.ToString().AsCString()); - error = mInfraIf.DiscoverNat64Prefix(); - -exit: - if (error != kErrorNone) - { - LogWarn("Failed to request infrastructure NAT64 prefix on %s: %s", mInfraIf.ToString().AsCString(), - ErrorToString(error)); - } -} - -void RoutingManager::UpdateInfraIfNat64Prefix(const Ip6::Prefix &aPrefix) -{ - mInfraIfNat64Prefix = aPrefix; - LogInfo("Get infrastructure NAT64 prefix: %s", - mInfraIfNat64Prefix.IsValidNat64() ? mInfraIfNat64Prefix.ToString().AsCString() : "none"); - - if (mIsRunning) - { - ScheduleRoutingPolicyEvaluation(kAfterRandomDelay); - } -} - -void RoutingManager::GenerateNat64Prefix(void) -{ - mLocalNat64Prefix = mBrUlaPrefix; - mLocalNat64Prefix.SetSubnetId(kNat64PrefixSubnetId); - mLocalNat64Prefix.mPrefix.mFields.m32[2] = 0; - mLocalNat64Prefix.SetLength(kNat64PrefixLength); - - LogInfo("Generated local NAT64 prefix: %s", mLocalNat64Prefix.ToString().AsCString()); -} -#endif - void RoutingManager::EvaluateState(void) { if (mIsEnabled && Get().IsAttached() && mInfraIf.IsRunning()) @@ -296,9 +247,9 @@ void RoutingManager::Start(void) mIsRunning = true; UpdateDiscoveredPrefixTableOnNetDataChange(); mLocalOnLinkPrefix.Start(); - StartRouterSolicitationDelay(); + mRsSender.Start(); #if OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE - mInfraIfNat64PrefixStaleTimer.Start(0); + mNat64PrefixManager.Start(); #endif } } @@ -315,14 +266,9 @@ void RoutingManager::Stop(void) mLocalOnLinkPrefix.Stop(); #if OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE - if (mPublishedNat64Prefix.IsValidNat64()) - { - UnpublishExternalRoute(mPublishedNat64Prefix); - } - mPublishedNat64Prefix.Clear(); - mInfraIfNat64Prefix.Clear(); - mInfraIfNat64PrefixStaleTimer.Stop(); + mNat64PrefixManager.Stop(); #endif + SendRouterAdvertisement(kInvalidateAllPrevPrefixes); mAdvertisedPrefixes.Clear(); @@ -332,8 +278,7 @@ void RoutingManager::Stop(void) mRaInfo.mTxCount = 0; - mRouterSolicitTimer.Stop(); - mRouterSolicitCount = 0; + mRsSender.Stop(); mRoutingPolicyTimer.Stop(); @@ -341,9 +286,35 @@ void RoutingManager::Stop(void) mIsRunning = false; +#if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE + if (Get().IsAutoEnableMode()) + { + Get().Disable(); + } +#endif + +exit: + return; +} + +#if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE +void RoutingManager::HandleSrpServerAutoEnableMode(void) +{ + VerifyOrExit(Get().IsAutoEnableMode()); + + if (IsInitalPolicyEvaluationDone()) + { + Get().Enable(); + } + else + { + Get().Disable(); + } + exit: return; } +#endif void RoutingManager::HandleReceived(const InfraIf::Icmp6Packet &aPacket, const Ip6::Address &aSrcAddress) { @@ -465,7 +436,7 @@ void RoutingManager::EvaluateOmrPrefix(void) { LogInfo("EvaluateOmrPrefix: No preferred OMR prefix found in Thread network"); - // The `aNewPrefixes` remains empty if we fail to publish + // The `mFavoredOmrPrefix` remains empty if we fail to publish // the local OMR prefix. SuccessOrExit(mLocalOmrPrefix.AddToNetData()); @@ -521,7 +492,7 @@ void RoutingManager::UnpublishExternalRoute(const Ip6::Prefix &aPrefix) void RoutingManager::EvaluateOnLinkPrefix(void) { - VerifyOrExit(!IsRouterSolicitationInProgress()); + VerifyOrExit(!mRsSender.IsInProgress()); mDiscoveredPrefixTable.FindFavoredOnLinkPrefix(mFavoredDiscoveredOnLinkPrefix); @@ -562,7 +533,7 @@ void RoutingManager::EvaluateOnLinkPrefix(void) // converge to the same smallest/favored on-link prefix and the // application-specific prefix is not used. - if (!(mLocalOmrPrefix.GetPrefix() < mFavoredDiscoveredOnLinkPrefix)) + if (!(mLocalOnLinkPrefix.GetPrefix() < mFavoredDiscoveredOnLinkPrefix)) { LogInfo("EvaluateOnLinkPrefix: There is already favored on-link prefix %s on %s", mFavoredDiscoveredOnLinkPrefix.ToString().AsCString(), mInfraIf.ToString().AsCString()); @@ -574,48 +545,6 @@ void RoutingManager::EvaluateOnLinkPrefix(void) return; } -#if OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE -void RoutingManager::EvaluateNat64Prefix(void) -{ - Ip6::Prefix nat64Prefix; - RoutePreference routePreference; - Error error; - NetworkData::ExternalRouteConfig preferredNat64PrefixConfig; - bool shouldPublish; - - OT_ASSERT(mIsRunning); - - LogInfo("Evaluating NAT64 prefix"); - - SuccessOrAssert(GetFavoredNat64Prefix(nat64Prefix, routePreference)); - error = Get().GetPreferredNat64Prefix(preferredNat64PrefixConfig); - - // NAT64 prefix is expected to be published from this BR when one of the following is true: - // - no NAT64 prefix exits in Network Data yet - // - the preferred NAT64 prefix in Network Data has lower preference than this BR's prefix - // - the preferred NAT64 prefix in Network Data was published by this BR - // - the preferred NAT64 prefix in Network Data is same as the infrastructure prefix - // TODO: change to check RLOC16 to determine if the NAT64 prefix was published by this BR - shouldPublish = (error == kErrorNotFound || preferredNat64PrefixConfig.mPreference < routePreference || - preferredNat64PrefixConfig.GetPrefix() == mPublishedNat64Prefix || - preferredNat64PrefixConfig.GetPrefix() == mInfraIfNat64Prefix); - - if (mPublishedNat64Prefix.IsValidNat64() && (!shouldPublish || nat64Prefix != mPublishedNat64Prefix)) - { - UnpublishExternalRoute(mPublishedNat64Prefix); - mPublishedNat64Prefix.Clear(); - } - if (shouldPublish && nat64Prefix != mPublishedNat64Prefix && - PublishExternalRoute(nat64Prefix, routePreference, /* aNat64= */ true) == kErrorNone) - { - mPublishedNat64Prefix = nat64Prefix; - } -#if OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE - Get().SetNat64Prefix(mPublishedNat64Prefix); -#endif -} -#endif - // This method evaluate the routing policy depends on prefix and route // information on Thread Network and infra link. As a result, this // method May send RA messages on infra link and publish/unpublish @@ -629,14 +558,38 @@ void RoutingManager::EvaluateRoutingPolicy(void) EvaluateOnLinkPrefix(); EvaluateOmrPrefix(); #if OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE - EvaluateNat64Prefix(); + mNat64PrefixManager.Evaluate(); #endif SendRouterAdvertisement(kAdvPrefixesFromNetData); +#if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE + if (Get().IsAutoEnableMode() && IsInitalPolicyEvaluationDone()) + { + // If SRP server uses the auto-enable mode, we enable the SRP + // server on the first RA transmission after we are done with + // initial prefix/route configurations. Note that if SRP server + // is already enabled, calling `Enable()` again does nothing. + + Get().Enable(); + } +#endif + ScheduleRoutingPolicyEvaluation(kForNextRa); } +bool RoutingManager::IsInitalPolicyEvaluationDone(void) const +{ + // This method indicates whether or not we are done with the + // initial policy evaluation and prefix and route setup, i.e., + // the OMR and on-link prefixes are determined, advertised in + // the emitted Router Advert message on infrastructure side + // and published in the Thread Network Data. + + return mIsRunning && !mFavoredOmrPrefix.IsEmpty() && + (mFavoredDiscoveredOnLinkPrefix.GetLength() != 0 || mLocalOnLinkPrefix.IsAdvertising()); +} + void RoutingManager::ScheduleRoutingPolicyEvaluation(ScheduleMode aMode) { TimeMilli now = TimerMilli::GetNow(); @@ -675,58 +628,18 @@ void RoutingManager::ScheduleRoutingPolicyEvaluation(ScheduleMode aMode) mRoutingPolicyTimer.FireAtIfEarlier(evaluateTime); } -// starts sending Router Solicitations in random delay -// between 0 and kMaxRtrSolicitationDelay. -void RoutingManager::StartRouterSolicitationDelay(void) -{ - uint32_t randomDelay; - - VerifyOrExit(!IsRouterSolicitationInProgress()); - - OT_ASSERT(mRouterSolicitCount == 0); - - static_assert(kMaxRtrSolicitationDelay > 0, "invalid maximum Router Solicitation delay"); - randomDelay = Random::NonCrypto::GetUint32InRange(0, Time::SecToMsec(kMaxRtrSolicitationDelay)); - - LogInfo("Start Router Solicitation, scheduled in %u milliseconds", randomDelay); - mTimeRouterSolicitStart = TimerMilli::GetNow(); - mRouterSolicitTimer.Start(randomDelay); - -exit: - return; -} - -bool RoutingManager::IsRouterSolicitationInProgress(void) const -{ - return mRouterSolicitTimer.IsRunning() || mRouterSolicitCount > 0; -} - -Error RoutingManager::SendRouterSolicitation(void) -{ - Ip6::Address destAddress; - Ip6::Nd::RouterSolicitMessage routerSolicit; - InfraIf::Icmp6Packet packet; - - OT_ASSERT(IsInitialized()); - - packet.InitFrom(routerSolicit); - destAddress.SetToLinkLocalAllRoutersMulticast(); - - return mInfraIf.Send(packet, destAddress); -} - void RoutingManager::SendRouterAdvertisement(RouterAdvTxMode aRaTxMode) { // RA message max length is derived to accommodate: // // - The RA header, - // - At most one PIO (for local on-link prefix), + // - At most two PIOs (for current and old local on-link prefixes), // - At most twice `kMaxOnMeshPrefixes` RIO for on-mesh prefixes. // Factor two is used for RIO to account for entries invalidating // previous prefixes while adding new ones. static constexpr uint16_t kMaxRaLength = - sizeof(Ip6::Nd::RouterAdvertMessage::Header) + sizeof(Ip6::Nd::PrefixInfoOption) + + sizeof(Ip6::Nd::RouterAdvertMessage::Header) + sizeof(Ip6::Nd::PrefixInfoOption) * 2 + 2 * kMaxOnMeshPrefixes * (sizeof(Ip6::Nd::RouteInfoOption) + sizeof(Ip6::Prefix)); uint8_t buffer[kMaxRaLength]; @@ -735,9 +648,10 @@ void RoutingManager::SendRouterAdvertisement(RouterAdvTxMode aRaTxMode) NetworkData::OnMeshPrefixConfig prefixConfig; // Append PIO for local on-link prefix if is either being - // advertised or deprecated. + // advertised or deprecated and for old prefix if is being + // deprecated. - mLocalOnLinkPrefix.AppendAsPioTo(raMsg); + mLocalOnLinkPrefix.AppendAsPiosTo(raMsg); // Determine which previously advertised prefixes need to be // invalidated. Under `kInvalidateAllPrevPrefixes` mode we need @@ -973,61 +887,25 @@ bool RoutingManager::IsValidOnLinkPrefix(const Ip6::Prefix &aOnLinkPrefix) !aOnLinkPrefix.IsMulticast(); } -void RoutingManager::HandleRouterSolicitTimer(Timer &aTimer) +void RoutingManager::HandleRsSenderFinished(TimeMilli aStartTime) { - aTimer.Get().HandleRouterSolicitTimer(); -} + // This is a callback from `RsSender` and is invoked when it + // finishes a cycle of sending Router Solicitations. `aStartTime` + // specifies the start time of the RS transmission cycle. + // + // We remove or deprecate old entries in discovered table that are + // not refreshed during Router Solicitation. We also invalidate + // the learned RA header if it is not refreshed during Router + // Solicitation. -void RoutingManager::HandleRouterSolicitTimer(void) -{ - LogInfo("Router solicitation times out"); + mDiscoveredPrefixTable.RemoveOrDeprecateOldEntries(aStartTime); - if (mRouterSolicitCount < kMaxRtrSolicitations) + if (mRaInfo.mHeaderUpdateTime <= aStartTime) { - uint32_t nextSolicitationDelay; - Error error; - - error = SendRouterSolicitation(); - - if (error == kErrorNone) - { - LogDebg("Successfully sent %uth Router Solicitation", mRouterSolicitCount); - ++mRouterSolicitCount; - nextSolicitationDelay = - (mRouterSolicitCount == kMaxRtrSolicitations) ? kMaxRtrSolicitationDelay : kRtrSolicitationInterval; - } - else - { - LogCrit("Failed to send %uth Router Solicitation: %s", mRouterSolicitCount, ErrorToString(error)); - - // It's unexpected that RS will fail and we will retry sending RS messages in 60 seconds. - // Notice that `mRouterSolicitCount` is not incremented for failed RS and thus we will - // not start configuring on-link prefixes before `kMaxRtrSolicitations` successful RS - // messages have been sent. - nextSolicitationDelay = kRtrSolicitationRetryDelay; - mRouterSolicitCount = 0; - } - - LogDebg("Router solicitation timer scheduled in %u seconds", nextSolicitationDelay); - mRouterSolicitTimer.Start(Time::SecToMsec(nextSolicitationDelay)); + UpdateRouterAdvertHeader(/* aRouterAdvertMessage */ nullptr); } - else - { - // Remove route prefixes and deprecate on-link prefixes that - // are not refreshed during Router Solicitation. - mDiscoveredPrefixTable.RemoveOrDeprecateOldEntries(mTimeRouterSolicitStart); - // Invalidate the learned RA message if it is not refreshed during Router Solicitation. - if (mRaInfo.mHeaderUpdateTime <= mTimeRouterSolicitStart) - { - UpdateRouterAdvertHeader(/* aRouterAdvertMessage */ nullptr); - } - - mRouterSolicitCount = 0; - - // Re-evaluate our routing policy and send Router Advertisement if necessary. - ScheduleRoutingPolicyEvaluation(kImmediately); - } + ScheduleRoutingPolicyEvaluation(kImmediately); } void RoutingManager::HandleDiscoveredPrefixStaleTimer(Timer &aTimer) @@ -1038,7 +916,7 @@ void RoutingManager::HandleDiscoveredPrefixStaleTimer(Timer &aTimer) void RoutingManager::HandleDiscoveredPrefixStaleTimer(void) { LogInfo("Stale On-Link or OMR Prefixes or RA messages are detected"); - StartRouterSolicitationDelay(); + mRsSender.Start(); } void RoutingManager::HandleRoutingPolicyTimer(Timer &aTimer) @@ -1046,21 +924,6 @@ void RoutingManager::HandleRoutingPolicyTimer(Timer &aTimer) aTimer.Get().EvaluateRoutingPolicy(); } -#if OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE -void RoutingManager::HandleInfraIfNat64PrefixStaleTimer(Timer &aTimer) -{ - aTimer.Get().HandleInfraIfNat64PrefixStaleTimer(); -} - -void RoutingManager::HandleInfraIfNat64PrefixStaleTimer(void) -{ - DiscoverInfraIfNat64Prefix(); - - mInfraIfNat64PrefixStaleTimer.Start(TimeMilli::SecToMsec(kDefaultNat64PrefixLifetime)); - LogInfo("NAT64 prefix timer scheduled in %u seconds", kDefaultNat64PrefixLifetime); -} -#endif - void RoutingManager::HandleRouterSolicit(const InfraIf::Icmp6Packet &aPacket, const Ip6::Address &aSrcAddress) { OT_UNUSED_VARIABLE(aPacket); @@ -2116,6 +1979,7 @@ RoutingManager::LocalOnLinkPrefix::LocalOnLinkPrefix(Instance &aInstance) , mTimer(aInstance, HandleTimer) { mPrefix.Clear(); + mOldPrefix.Clear(); } void RoutingManager::LocalOnLinkPrefix::Generate(void) @@ -2139,9 +2003,18 @@ void RoutingManager::LocalOnLinkPrefix::Start(void) void RoutingManager::LocalOnLinkPrefix::Stop(void) { + mTimer.Stop(); + + if (mOldPrefix.GetLength() != 0) + { + Get().UnpublishExternalRoute(mOldPrefix); + mOldPrefix.Clear(); + } + VerifyOrExit(mState != kIdle); Get().UnpublishExternalRoute(mPrefix); + mState = kDeprecating; // Start deprecating the local on-link prefix to send a PIO @@ -2162,11 +2035,10 @@ Error RoutingManager::LocalOnLinkPrefix::Advertise(void) VerifyOrExit(mState != kAdvertising); - mTimer.Stop(); - SuccessOrExit(error = Get().PublishExternalRoute(mPrefix, NetworkData::kRoutePreferenceMedium)); - mState = kAdvertising; + mState = kAdvertising; + mExpireTime = TimerMilli::GetNow() + TimeMilli::SecToMsec(kDefaultOnLinkPrefixLifetime); LogInfo("Start advertising on-link prefix %s", mPrefix.ToString().AsCString()); exit: @@ -2185,36 +2057,41 @@ void RoutingManager::LocalOnLinkPrefix::Deprecate(void) VerifyOrExit(mState == kAdvertising); mState = kDeprecating; + mTimer.FireAtIfEarlier(mExpireTime); LogInfo("Deprecate local on-link prefix %s", mPrefix.ToString().AsCString()); exit: return; } -void RoutingManager::LocalOnLinkPrefix::AppendAsPioTo(Ip6::Nd::RouterAdvertMessage &aRaMessage) +void RoutingManager::LocalOnLinkPrefix::AppendAsPiosTo(Ip6::Nd::RouterAdvertMessage &aRaMessage) +{ + AppendCurPrefix(aRaMessage); + AppendOldPrefix(aRaMessage); +} + +void RoutingManager::LocalOnLinkPrefix::AppendCurPrefix(Ip6::Nd::RouterAdvertMessage &aRaMessage) { // Append the local on-link prefix to the `aRaMessage` as a PIO // only if it is being advertised or deprecated. // - // If in `kAdvertising` state, we restart the lifetime timer. + // If in `kAdvertising` state, we reset the expire time. // If in `kDeprecating` state, we include it as PIO with zero - // preferred lifetime and the remaining valid lifetime which - // is tracked by the timer. + // preferred lifetime and the remaining valid lifetime. uint32_t validLifetime = kDefaultOnLinkPrefixLifetime; uint32_t preferredLifetime = kDefaultOnLinkPrefixLifetime; - TimeMilli now; + TimeMilli now = TimerMilli::GetNow(); switch (mState) { case kAdvertising: - mTimer.Start(TimeMilli::SecToMsec(kDefaultOnLinkPrefixLifetime)); + mExpireTime = now + TimeMilli::SecToMsec(kDefaultOnLinkPrefixLifetime); break; case kDeprecating: - now = TimerMilli::GetNow(); - VerifyOrExit(mTimer.IsRunning() && (mTimer.GetFireTime() > now)); - validLifetime = TimeMilli::MsecToSec(mTimer.GetFireTime() - now); + VerifyOrExit(mExpireTime > now); + validLifetime = TimeMilli::MsecToSec(mExpireTime - now); preferredLifetime = 0; break; @@ -2231,22 +2108,41 @@ void RoutingManager::LocalOnLinkPrefix::AppendAsPioTo(Ip6::Nd::RouterAdvertMessa return; } +void RoutingManager::LocalOnLinkPrefix::AppendOldPrefix(Ip6::Nd::RouterAdvertMessage &aRaMessage) +{ + TimeMilli now = TimerMilli::GetNow(); + uint32_t validLifetime; + + VerifyOrExit((mOldPrefix.GetLength() != 0) && (mOldExpireTime > now)); + + validLifetime = TimeMilli::MsecToSec(mOldExpireTime - now); + SuccessOrAssert(aRaMessage.AppendPrefixInfoOption(mOldPrefix, validLifetime, 0)); + + LogInfo("RouterAdvert: Added PIO for %s (valid=%u, preferred=0)", mOldPrefix.ToString().AsCString(), validLifetime); + +exit: + return; +} + void RoutingManager::LocalOnLinkPrefix::HandleExtPanIdChange(void) { + // If the prefix is advertised or being deprecated we remember it + // as `mOldPrefix` and deprecate it. It will be included in + // emitted RAs as PIO with zero preferred lifetime. It will still + // be present in Network Data until its expire time so to allow + // Thread nodes to continue to communicate with `InfraIf` devices + // using addresses based on this prefix. + switch (mState) { case kIdle: break; case kAdvertising: - Get().UnpublishExternalRoute(mPrefix); - - // TODO: consider deprecating/invalidating the existing - // on-link prefix. - break; - case kDeprecating: - mTimer.Stop(); + mOldPrefix = mPrefix; + mOldExpireTime = mExpireTime; + mTimer.FireAtIfEarlier(mOldExpireTime); break; } @@ -2261,19 +2157,41 @@ void RoutingManager::LocalOnLinkPrefix::HandleTimer(Timer &aTimer) void RoutingManager::LocalOnLinkPrefix::HandleTimer(void) { - VerifyOrExit(mState == kDeprecating); + TimeMilli now = TimerMilli::GetNow(); - LogInfo("Local on-link prefix %s expired", mPrefix.ToString().AsCString()); + if ((mState == kDeprecating) && (now >= mExpireTime)) + { + LogInfo("Local on-link prefix %s expired", mPrefix.ToString().AsCString()); + Unpublish(mPrefix); + mState = kIdle; + } - if (!Get().mDiscoveredPrefixTable.ContainsOnLinkPrefix(mPrefix)) + if ((mOldPrefix.GetLength() != 0) && (now >= mOldExpireTime)) { - Get().UnpublishExternalRoute(mPrefix); + LogInfo("Old local on-link prefix %s expired", mOldPrefix.ToString().AsCString()); + Unpublish(mOldPrefix); + mOldPrefix.Clear(); } - mState = kIdle; + // Re-schedule the timer -exit: - return; + if (mState == kDeprecating) + { + mTimer.FireAt(mExpireTime); + } + + if (mOldPrefix.GetLength() != 0) + { + mTimer.FireAtIfEarlier(mOldExpireTime); + } +} + +void RoutingManager::LocalOnLinkPrefix::Unpublish(const Ip6::Prefix &aPrefix) +{ + if (!Get().mDiscoveredPrefixTable.ContainsOnLinkPrefix(aPrefix)) + { + Get().UnpublishExternalRoute(aPrefix); + } } //--------------------------------------------------------------------------------------------------------------------- @@ -2312,6 +2230,237 @@ void RoutingManager::OnMeshPrefixArray::MarkAsDeleted(const OnMeshPrefix &aPrefi } } +#if OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE + +//--------------------------------------------------------------------------------------------------------------------- +// Nat64PrefixManager + +RoutingManager::Nat64PrefixManager::Nat64PrefixManager(Instance &aInstance) + : InstanceLocator(aInstance) + , mTimer(aInstance, HandleTimer) +{ + mInfraIfPrefix.Clear(); + mLocalPrefix.Clear(); + mPublishedPrefix.Clear(); +} + +void RoutingManager::Nat64PrefixManager::Start(void) +{ + mTimer.Start(0); +} + +void RoutingManager::Nat64PrefixManager::Stop(void) +{ + if (mPublishedPrefix.IsValidNat64()) + { + Get().UnpublishExternalRoute(mPublishedPrefix); + } + + mPublishedPrefix.Clear(); + mInfraIfPrefix.Clear(); + mTimer.Stop(); +} + +void RoutingManager::Nat64PrefixManager::GenerateLocalPrefix(const Ip6::Prefix &aBrUlaPrefix) +{ + mLocalPrefix = aBrUlaPrefix; + mLocalPrefix.SetSubnetId(kNat64PrefixSubnetId); + mLocalPrefix.mPrefix.mFields.m32[2] = 0; + mLocalPrefix.SetLength(kNat64PrefixLength); + + LogInfo("Generated local NAT64 prefix: %s", mLocalPrefix.ToString().AsCString()); +} + +const Ip6::Prefix &RoutingManager::Nat64PrefixManager::GetFavoredPrefix(RoutePreference &aPreference) const +{ + const Ip6::Prefix *favoredPrefix = &mInfraIfPrefix; + + if (mInfraIfPrefix.IsValidNat64()) + { + aPreference = NetworkData::kRoutePreferenceMedium; + } + else + { + favoredPrefix = &mLocalPrefix; + aPreference = NetworkData::kRoutePreferenceLow; + } + + return *favoredPrefix; +} + +void RoutingManager::Nat64PrefixManager::Evaluate(void) +{ + Error error; + Ip6::Prefix prefix; + RoutePreference preference; + NetworkData::ExternalRouteConfig netdataPrefixConfig; + bool shouldPublish; + + LogInfo("Evaluating NAT64 prefix"); + + prefix = GetFavoredPrefix(preference); + + error = Get().GetPreferredNat64Prefix(netdataPrefixConfig); + + // NAT64 prefix is expected to be published from this BR + // when one of the following is true: + // + // - No NAT64 prefix in Network Data. + // - The preferred NAT64 prefix in Network Data has lower + // preference than this BR's prefix. + // - The preferred NAT64 prefix in Network Data was published + // by this BR. + // - The preferred NAT64 prefix in Network Data is same as the + // discovered infrastructure prefix. + // + // TODO: change to check RLOC16 to determine if the NAT64 prefix + // was published by this BR. + + shouldPublish = + ((error == kErrorNotFound) || (netdataPrefixConfig.mPreference < preference) || + (netdataPrefixConfig.GetPrefix() == mPublishedPrefix) || (netdataPrefixConfig.GetPrefix() == mInfraIfPrefix)); + + if (mPublishedPrefix.IsValidNat64() && (!shouldPublish || (prefix != mPublishedPrefix))) + { + Get().UnpublishExternalRoute(mPublishedPrefix); + mPublishedPrefix.Clear(); + } + + if (shouldPublish && (prefix != mPublishedPrefix) && + (Get().PublishExternalRoute(prefix, preference, /* aNat64 */ true) == kErrorNone)) + { + mPublishedPrefix = prefix; + } + +#if OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE + Get().SetNat64Prefix(mPublishedPrefix); +#endif +} + +void RoutingManager::Nat64PrefixManager::HandleTimer(Timer &aTimer) +{ + aTimer.Get().mNat64PrefixManager.HandleTimer(); +} + +void RoutingManager::Nat64PrefixManager::HandleTimer(void) +{ + Discover(); + + mTimer.Start(TimeMilli::SecToMsec(kDefaultNat64PrefixLifetime)); + LogInfo("NAT64 prefix timer scheduled in %u seconds", kDefaultNat64PrefixLifetime); +} + +void RoutingManager::Nat64PrefixManager::Discover(void) +{ + Error error = Get().mInfraIf.DiscoverNat64Prefix(); + + if (error == kErrorNone) + { + LogInfo("Discovering infraif NAT64 prefix"); + } + else + { + LogWarn("Failed to discover infraif NAT64 prefix: %s", ErrorToString(error)); + } +} + +void RoutingManager::Nat64PrefixManager::HandleDiscoverDone(const Ip6::Prefix &aPrefix) +{ + mInfraIfPrefix = aPrefix; + + LogInfo("Infraif NAT64 prefix: %s", mInfraIfPrefix.IsValidNat64() ? mInfraIfPrefix.ToString().AsCString() : "none"); + + if (Get().mIsRunning) + { + Get().ScheduleRoutingPolicyEvaluation(kAfterRandomDelay); + } +} + +#endif // OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE + +//--------------------------------------------------------------------------------------------------------------------- +// RsSender + +RoutingManager::RsSender::RsSender(Instance &aInstance) + : InstanceLocator(aInstance) + , mTxCount(0) + , mTimer(aInstance, HandleTimer) +{ +} + +void RoutingManager::RsSender::Start(void) +{ + uint32_t delay; + + VerifyOrExit(!IsInProgress()); + + delay = Random::NonCrypto::GetUint32InRange(0, kMaxStartDelay); + LogInfo("Scheduled Router Solicitation in %u milliseconds", delay); + + mTxCount = 0; + mStartTime = TimerMilli::GetNow(); + mTimer.Start(delay); + +exit: + return; +} + +void RoutingManager::RsSender::Stop(void) +{ + mTimer.Stop(); +} + +Error RoutingManager::RsSender::SendRs(void) +{ + Ip6::Address destAddress; + Ip6::Nd::RouterSolicitMessage routerSolicit; + InfraIf::Icmp6Packet packet; + + packet.InitFrom(routerSolicit); + destAddress.SetToLinkLocalAllRoutersMulticast(); + + return Get().mInfraIf.Send(packet, destAddress); +} + +void RoutingManager::RsSender::HandleTimer(Timer &aTimer) +{ + aTimer.Get().mRsSender.HandleTimer(); +} + +void RoutingManager::RsSender::HandleTimer(void) +{ + Error error; + uint32_t delay; + + if (mTxCount >= kMaxTxCount) + { + Get().HandleRsSenderFinished(mStartTime); + ExitNow(); + } + + error = SendRs(); + + if (error == kErrorNone) + { + mTxCount++; + LogInfo("Successfully sent RS %d/%d", mTxCount, kMaxTxCount); + delay = (mTxCount == kMaxTxCount) ? kWaitOnLastAttempt : kTxInterval; + } + else + { + LogCrit("Failed to send RS %d, error:%s", mTxCount + 1, ErrorToString(error)); + + // Note that `mTxCount` is intentionally not incremented + // if the tx fails. + delay = kRetryDelay; + } + + mTimer.Start(delay); + +exit: + return; +} + } // namespace BorderRouter } // namespace ot diff --git a/src/core/border_router/routing_manager.hpp b/src/core/border_router/routing_manager.hpp index e3a0087cd4b..88003206539 100644 --- a/src/core/border_router/routing_manager.hpp +++ b/src/core/border_router/routing_manager.hpp @@ -230,12 +230,14 @@ class RoutingManager : public InstanceLocator Error GetFavoredNat64Prefix(Ip6::Prefix &aPrefix, RoutePreference &aRoutePreference); /** - * This method updates mInfraIfNat64Prefix to @p aPrefix. + * This method informs `RoutingManager` of the result of the discovery request of NAT64 prefix on infrastructure + * interface (`InfraIf::DiscoverNat64Prefix()`). * - * @param[in] aPrefix A NAT64 prefix on infrastructure link. + * @param[in] aPrefix The discovered NAT64 prefix on `InfraIf`. * */ - void UpdateInfraIfNat64Prefix(const Ip6::Prefix &aPrefix); + void HandleDiscoverNat64PrefixDone(const Ip6::Prefix &aPrefix) { mNat64PrefixManager.HandleDiscoverDone(aPrefix); } + #endif // OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE /** @@ -308,6 +310,16 @@ class RoutingManager : public InstanceLocator return mDiscoveredPrefixTable.GetNextEntry(aIterator, aEntry); } +#if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE + /** + * This method determines whether to enable/disable SRP server when the auto-enable mode is changed on SRP server. + * + * This should be called from `Srp::Server` when auto-enable mode is changed. + * + */ + void HandleSrpServerAutoEnableMode(void); +#endif + private: static constexpr uint8_t kMaxOnMeshPrefixes = OPENTHREAD_CONFIG_BORDER_ROUTING_MAX_ON_MESH_PREFIXES; @@ -322,9 +334,6 @@ class RoutingManager : public InstanceLocator // The maximum number of initial Router Advertisements. static constexpr uint32_t kMaxInitRtrAdvertisements = 3; - // The maximum number of Router Solicitations before sending Router Advertisements. - static constexpr uint32_t kMaxRtrSolicitations = 3; - static constexpr uint32_t kDefaultOmrPrefixLifetime = 1800; // The default OMR prefix valid lifetime. In sec. static constexpr uint32_t kDefaultOnLinkPrefixLifetime = 1800; // The default on-link prefix valid lifetime. In sec. static constexpr uint32_t kDefaultNat64PrefixLifetime = 300; // The default NAT64 prefix valid lifetime. In sec. @@ -332,13 +341,9 @@ class RoutingManager : public InstanceLocator static constexpr uint32_t kMinRtrAdvInterval = kMaxRtrAdvInterval / 3; // Min RA Interval. In sec. static constexpr uint32_t kMaxInitRtrAdvInterval = 16; // Max Initial RA Interval. In sec. static constexpr uint32_t kRaReplyJitter = 500; // Jitter for sending RA after rx RS. In msec. - static constexpr uint32_t kRtrSolicitationInterval = 4; // Interval between RSs. In sec. - static constexpr uint32_t kMaxRtrSolicitationDelay = 1; // Max delay for initial solicitation. In sec. static constexpr uint32_t kPolicyEvaluationMinDelay = 2000; // Min delay for policy evaluation. In msec. static constexpr uint32_t kPolicyEvaluationMaxDelay = 4000; // Max delay for policy evaluation. In msec. - static constexpr uint32_t kRtrSolicitationRetryDelay = - kRtrSolicitationInterval; // The delay before retrying failed RS tx. In Sec. - static constexpr uint32_t kMinDelayBetweenRtrAdvs = 3000; // Min delay (msec) between consecutive RAs. + static constexpr uint32_t kMinDelayBetweenRtrAdvs = 3000; // Min delay (msec) between consecutive RAs. // The STALE_RA_TIME in seconds. The Routing Manager will consider the prefixes // and learned RA parameters STALE when they are not refreshed in STALE_RA_TIME @@ -597,7 +602,7 @@ class RoutingManager : public InstanceLocator void Stop(void); Error Advertise(void); void Deprecate(void); - void AppendAsPioTo(Ip6::Nd::RouterAdvertMessage &aRaMessage); + void AppendAsPiosTo(Ip6::Nd::RouterAdvertMessage &aRaMessage); const Ip6::Prefix &GetPrefix(void) const { return mPrefix; } bool IsAdvertising(void) const { return (mState == kAdvertising); } void HandleExtPanIdChange(void); @@ -610,11 +615,18 @@ class RoutingManager : public InstanceLocator kDeprecating, }; + void AppendCurPrefix(Ip6::Nd::RouterAdvertMessage &aRaMessage); + void AppendOldPrefix(Ip6::Nd::RouterAdvertMessage &aRaMessage); + void Unpublish(const Ip6::Prefix &aPrefix); + static void HandleTimer(Timer &aTimer); void HandleTimer(void); Ip6::Prefix mPrefix; State mState; + TimeMilli mExpireTime; + Ip6::Prefix mOldPrefix; + TimeMilli mOldExpireTime; TimerMilli mTimer; }; @@ -627,6 +639,38 @@ class RoutingManager : public InstanceLocator void MarkAsDeleted(const OnMeshPrefix &aPrefix); }; +#if OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE + class Nat64PrefixManager : public InstanceLocator + { + public: + // This class manages the NAT64 related functions including + // generation of local NAT64 prefix, discovery of infra + // interface prefix, maintaining the discovered prefix + // lifetime, and selection of the NAT64 prefix to publish in + // Network Data. + + explicit Nat64PrefixManager(Instance &aInstance); + + void Start(void); + void Stop(void); + void GenerateLocalPrefix(const Ip6::Prefix &aBrUlaPrefix); + const Ip6::Prefix &GetLocalPrefix(void) const { return mLocalPrefix; } + const Ip6::Prefix &GetFavoredPrefix(RoutePreference &aPreference) const; + void Evaluate(void); + void HandleDiscoverDone(const Ip6::Prefix &aPrefix); + + private: + void Discover(void); + static void HandleTimer(Timer &aTimer); + void HandleTimer(void); + + Ip6::Prefix mInfraIfPrefix; // The latest NAT64 prefix discovered on the infrastructure interface. + Ip6::Prefix mLocalPrefix; // The local prefix (from BR ULA prefix). + Ip6::Prefix mPublishedPrefix; // The prefix published in Network Data (may be empty or local or from infra-if). + TimerMilli mTimer; + }; +#endif // OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE + struct RaInfo { // Tracks info about emitted RA messages: Number of RAs sent, @@ -651,6 +695,39 @@ class RoutingManager : public InstanceLocator TimeMilli mLastTxTime; }; + class RsSender : public InstanceLocator + { + public: + // This class implements tx of Router Solicitation (RS) + // messages to discover other routers. `Start()` schedules + // a cycle of RS transmissions of `kMaxTxCount` separated + // by `kTxInterval`. At the end of cycle the callback + // `HandleRsSenderFinished()` is invoked to inform end of + // the cycle to `RoutingManager`. + + explicit RsSender(Instance &aInstance); + + bool IsInProgress(void) const { return mTimer.IsRunning(); } + void Start(void); + void Stop(void); + + private: + // All time intervals are in msec. + static constexpr uint32_t kMaxStartDelay = 1000; // Max random delay to send the first RS. + static constexpr uint32_t kTxInterval = 4000; // Interval between RS tx. + static constexpr uint32_t kRetryDelay = kTxInterval; // Interval to wait to retry a failed RS tx. + static constexpr uint32_t kWaitOnLastAttempt = 1000; // Wait interval after last RS tx. + static constexpr uint8_t kMaxTxCount = 3; // Number of RS tx in one cycle. + + Error SendRs(void); + static void HandleTimer(Timer &aTimer); + void HandleTimer(void); + + uint8_t mTxCount; + TimerMilli mTimer; + TimeMilli mStartTime; + }; + void EvaluateState(void); void Start(void); void Stop(void); @@ -659,37 +736,22 @@ class RoutingManager : public InstanceLocator bool IsEnabled(void) const { return mIsEnabled; } Error LoadOrGenerateRandomBrUlaPrefix(void); - void EvaluateOnLinkPrefix(void); - -#if OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE - void DiscoverInfraIfNat64Prefix(void); - void GenerateNat64Prefix(void); - void EvaluateNat64Prefix(void); -#endif - + void EvaluateOnLinkPrefix(void); void EvaluateRoutingPolicy(void); + bool IsInitalPolicyEvaluationDone(void) const; void ScheduleRoutingPolicyEvaluation(ScheduleMode aMode); void EvaluateOmrPrefix(void); Error PublishExternalRoute(const Ip6::Prefix &aPrefix, RoutePreference aRoutePreference, bool aNat64 = false); void UnpublishExternalRoute(const Ip6::Prefix &aPrefix); - void StartRouterSolicitationDelay(void); - Error SendRouterSolicitation(void); + void HandleRsSenderFinished(TimeMilli aStartTime); void SendRouterAdvertisement(RouterAdvTxMode aRaTxMode); - bool IsRouterSolicitationInProgress(void) const; - static void HandleRouterSolicitTimer(Timer &aTimer); - void HandleRouterSolicitTimer(void); static void HandleDiscoveredPrefixInvalidTimer(Timer &aTimer); void HandleDiscoveredPrefixInvalidTimer(void); static void HandleDiscoveredPrefixStaleTimer(Timer &aTimer); void HandleDiscoveredPrefixStaleTimer(void); static void HandleRoutingPolicyTimer(Timer &aTimer); -#if OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE - static void HandleInfraIfNat64PrefixStaleTimer(Timer &aTimer); - void HandleInfraIfNat64PrefixStaleTimer(void); -#endif - void DeprecateOnLinkPrefix(void); void HandleRouterSolicit(const InfraIf::Icmp6Packet &aPacket, const Ip6::Address &aSrcAddress); void HandleRouterAdvertisement(const InfraIf::Icmp6Packet &aPacket, const Ip6::Address &aSrcAddress); bool ShouldProcessPrefixInfoOption(const Ip6::Nd::PrefixInfoOption &aPio, const Ip6::Prefix &aPrefix); @@ -735,27 +797,13 @@ class RoutingManager : public InstanceLocator DiscoveredPrefixTable mDiscoveredPrefixTable; #if OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE - // The latest NAT64 prefix discovered on the infrastructure interface. - Ip6::Prefix mInfraIfNat64Prefix; - // The NAT64 prefix allocated from the /48 BR ULA prefix. - Ip6::Prefix mLocalNat64Prefix; - // The NAT64 prefix published in Network Data. It can have the following value: - // - empty: no NAT64 prefix is published from this BR - // - the local NAT64 prefix - // - the latest published infrastructure NAT64 prefix, which might differs from mInfraIfNat64Prefix - Ip6::Prefix mPublishedNat64Prefix; - - TimerMilli mInfraIfNat64PrefixStaleTimer; + Nat64PrefixManager mNat64PrefixManager; #endif - RaInfo mRaInfo; + RaInfo mRaInfo; + RsSender mRsSender; TimerMilli mDiscoveredPrefixStaleTimer; - - TimerMilli mRouterSolicitTimer; - TimeMilli mTimeRouterSolicitStart; - uint8_t mRouterSolicitCount; - TimerMilli mRoutingPolicyTimer; }; diff --git a/src/core/config/srp_client.h b/src/core/config/srp_client.h index a8036757b14..ca609abeb43 100644 --- a/src/core/config/srp_client.h +++ b/src/core/config/srp_client.h @@ -35,6 +35,8 @@ #ifndef CONFIG_SRP_CLIENT_H_ #define CONFIG_SRP_CLIENT_H_ +#include "config/misc.h" + /** * @def OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE * diff --git a/src/core/ftd.cmake b/src/core/ftd.cmake index 0100a1ffd6b..8bf3e0e6a85 100644 --- a/src/core/ftd.cmake +++ b/src/core/ftd.cmake @@ -47,5 +47,5 @@ target_link_libraries(openthread-ftd ) if(NOT OT_EXCLUDE_TCPLP_LIB) - target_link_libraries(openthread-ftd PRIVATE tcplp) + target_link_libraries(openthread-ftd PRIVATE tcplp-ftd) endif() diff --git a/src/core/meshcop/dtls.cpp b/src/core/meshcop/dtls.cpp index aae1c5d81aa..790c78e7d42 100644 --- a/src/core/meshcop/dtls.cpp +++ b/src/core/meshcop/dtls.cpp @@ -184,17 +184,11 @@ void Dtls::HandleUdpReceive(Message &aMessage, const Ip6::MessageInfo &aMessageI ExitNow(); case Dtls::kStateOpen: - IgnoreError(mSocket.Connect(Ip6::SockAddr(aMessageInfo.GetPeerAddr(), aMessageInfo.GetPeerPort()))); - mMessageInfo.SetPeerAddr(aMessageInfo.GetPeerAddr()); mMessageInfo.SetPeerPort(aMessageInfo.GetPeerPort()); mMessageInfo.SetIsHostInterface(aMessageInfo.IsHostInterface()); - if (Get().HasUnicastAddress(aMessageInfo.GetSockAddr())) - { - mMessageInfo.SetSockAddr(aMessageInfo.GetSockAddr()); - } - + mMessageInfo.SetSockAddr(aMessageInfo.GetSockAddr()); mMessageInfo.SetSockPort(aMessageInfo.GetSockPort()); SuccessOrExit(Setup(false)); diff --git a/src/core/mtd.cmake b/src/core/mtd.cmake index 7d6f0ef8b80..feec81e4151 100644 --- a/src/core/mtd.cmake +++ b/src/core/mtd.cmake @@ -47,5 +47,5 @@ target_link_libraries(openthread-mtd ) if(NOT OT_EXCLUDE_TCPLP_LIB) - target_link_libraries(openthread-mtd PRIVATE tcplp) + target_link_libraries(openthread-mtd PRIVATE tcplp-mtd) endif() diff --git a/src/core/net/ip4_types.cpp b/src/core/net/ip4_types.cpp index a23f51e3325..47e4841969b 100644 --- a/src/core/net/ip4_types.cpp +++ b/src/core/net/ip4_types.cpp @@ -127,20 +127,44 @@ void Address::SynthesizeFromCidrAndHost(const Cidr &aCidr, const uint32_t aHost) mFields.m32 = (aCidr.mAddress.mFields.m32 & aCidr.SubnetMask()) | (HostSwap32(aHost) & aCidr.HostMask()); } +void Address::ToString(StringWriter &aWriter) const +{ + aWriter.Append("%d.%d.%d.%d", mFields.m8[0], mFields.m8[1], mFields.m8[2], mFields.m8[3]); +} + +void Address::ToString(char *aBuffer, uint16_t aSize) const +{ + StringWriter writer(aBuffer, aSize); + + ToString(writer); +} + Address::InfoString Address::ToString(void) const { InfoString string; - string.Append("%d.%d.%d.%d", mFields.m8[0], mFields.m8[1], mFields.m8[2], mFields.m8[3]); + ToString(string); return string; } +void Cidr::ToString(StringWriter &aWriter) const +{ + aWriter.Append("%s/%d", AsCoreType(&mAddress).ToString().AsCString(), mLength); +} + +void Cidr::ToString(char *aBuffer, uint16_t aSize) const +{ + StringWriter writer(aBuffer, aSize); + + ToString(writer); +} + Cidr::InfoString Cidr::ToString(void) const { InfoString string; - string.Append("%s/%d", AsCoreType(&mAddress).ToString().AsCString(), mLength); + ToString(string); return string; } diff --git a/src/core/net/ip4_types.hpp b/src/core/net/ip4_types.hpp index 042e4ca4980..fa4bb416506 100644 --- a/src/core/net/ip4_types.hpp +++ b/src/core/net/ip4_types.hpp @@ -161,6 +161,20 @@ class Address : public otIp4Address, public Equatable
, public Clearable */ Error FromString(const char *aString); + /** + * This method converts the address to a string. + * + * The string format uses quad-dotted notation of four bytes in the address (e.g., "127.0.0.1"). + * + * If the resulting string does not fit in @p aBuffer (within its @p aSize characters), the string will be + * truncated but the outputted string is always null-terminated. + * + * @param[out] aBuffer A pointer to a char array to output the string (MUST NOT be `nullptr`). + * @param[in] aSize The size of @p aBuffer (in bytes). + * + */ + void ToString(char *aBuffer, uint16_t aSize) const; + /** * This method converts the IPv4 address to a string. * @@ -170,6 +184,9 @@ class Address : public otIp4Address, public Equatable
, public Clearable * */ InfoString ToString(void) const; + +private: + void ToString(StringWriter &aWriter) const; } OT_TOOL_PACKED_END; /** @@ -189,6 +206,21 @@ class Cidr : public otIp4Cidr, public Unequatable, public Clearable InfoString; + /** + * This method converts the IPv4 CIDR to a string. + * + * The string format uses quad-dotted notation of four bytes in the address with the length of prefix (e.g., + * "127.0.0.1/32"). + * + * If the resulting string does not fit in @p aBuffer (within its @p aSize characters), the string will be + * truncated but the outputted string is always null-terminated. + * + * @param[out] aBuffer A pointer to a char array to output the string (MUST NOT be `nullptr`). + * @param[in] aSize The size of @p aBuffer (in bytes). + * + */ + void ToString(char *aBuffer, uint16_t aSize) const; + /** * This method converts the IPv4 CIDR to a string. * @@ -237,6 +269,8 @@ class Cidr : public otIp4Cidr, public Unequatable, public Clearable(&mNextMappingId), sizeof(mNextMappingId)); + mNat64Prefix.Clear(); mIp4Cidr.Clear(); mMappingExpirer.Start(kAddressMappingIdleTimeoutMsec); @@ -91,10 +93,11 @@ Error Translator::SendMessage(Message &aMessage) Translator::Result Translator::TranslateFromIp6(Message &aMessage) { - Result res = kDrop; - Ip6::Header ip6Header; - Ip4::Header ip4Header; - AddressMapping *mapping = nullptr; + Result res = kDrop; + ErrorCounters::Reason dropReason = ErrorCounters::kUnknown; + Ip6::Header ip6Header; + Ip4::Header ip4Header; + AddressMapping * mapping = nullptr; if (mIp4Cidr.mLength == 0 || !mNat64Prefix.IsValidNat64()) { @@ -105,6 +108,7 @@ Translator::Result Translator::TranslateFromIp6(Message &aMessage) if (ip6Header.ParseFrom(aMessage) != kErrorNone) { LogWarn("outgoing datagram is not a valid IPv6 datagram, drop"); + dropReason = ErrorCounters::Reason::kIllegalPacket; ExitNow(res = kDrop); } @@ -117,6 +121,7 @@ Translator::Result Translator::TranslateFromIp6(Message &aMessage) if (mapping == nullptr) { LogWarn("failed to get a mapping for %s (mapping pool full?)", ip6Header.GetSource().ToString().AsCString()); + dropReason = ErrorCounters::Reason::kNoMapping; ExitNow(res = kDrop); } @@ -145,6 +150,7 @@ Translator::Result Translator::TranslateFromIp6(Message &aMessage) res = kForward; break; default: + dropReason = ErrorCounters::Reason::kUnsupportedProto; ExitNow(res = kDrop); } @@ -161,17 +167,24 @@ Translator::Result Translator::TranslateFromIp6(Message &aMessage) ExitNow(res = kDrop); } aMessage.SetType(Message::kTypeIp4); + mCounters.Count6To4Packet(ip6Header.GetNextHeader(), ip6Header.GetPayloadLength()); + mapping->mCounters.Count6To4Packet(ip6Header.GetNextHeader(), ip6Header.GetPayloadLength()); exit: + if (res == Result::kDrop) + { + mErrorCounters.Count6To4(dropReason); + } return res; } Translator::Result Translator::TranslateToIp6(Message &aMessage) { - Result res = Result::kDrop; - Ip6::Header ip6Header; - Ip4::Header ip4Header; - AddressMapping *mapping = nullptr; + Result res = Result::kDrop; + ErrorCounters::Reason dropReason = ErrorCounters::kUnknown; + Ip6::Header ip6Header; + Ip4::Header ip4Header; + AddressMapping * mapping = nullptr; // Ip6::Header::ParseFrom may return an error value when the incoming message is an IPv4 datagram. // If the message is already an IPv6 datagram, forward it directly. @@ -193,6 +206,7 @@ Translator::Result Translator::TranslateToIp6(Message &aMessage) if (ip4Header.ParseFrom(aMessage) != kErrorNone) { LogWarn("incoming message is neither IPv4 nor an IPv6 datagram, drop"); + dropReason = ErrorCounters::Reason::kIllegalPacket; ExitNow(res = kDrop); } @@ -200,6 +214,7 @@ Translator::Result Translator::TranslateToIp6(Message &aMessage) if (mapping == nullptr) { LogWarn("no mapping found for the IPv4 address"); + dropReason = ErrorCounters::Reason::kNoMapping; ExitNow(res = kDrop); } @@ -230,6 +245,7 @@ Translator::Result Translator::TranslateToIp6(Message &aMessage) res = kForward; break; default: + dropReason = ErrorCounters::Reason::kUnsupportedProto; ExitNow(res = kDrop); } @@ -245,8 +261,15 @@ Translator::Result Translator::TranslateToIp6(Message &aMessage) ExitNow(res = kDrop); } aMessage.SetType(Message::kTypeIp6); + mCounters.Count4To6Packet(ip4Header.GetProtocol(), ip4Header.GetTotalLength() - sizeof(ip4Header)); + mapping->mCounters.Count4To6Packet(ip4Header.GetProtocol(), ip4Header.GetTotalLength() - sizeof(ip4Header)); exit: + if (res == Result::kDrop) + { + mErrorCounters.Count4To6(dropReason); + } + return res; } @@ -259,6 +282,25 @@ Translator::AddressMapping::InfoString Translator::AddressMapping::ToString(void return string; } +void Translator::AddressMapping::CopyTo(otNat64AddressMapping &aMapping, TimeMilli aNow) const +{ + aMapping.mId = mId; + aMapping.mIp4 = mIp4; + aMapping.mIp6 = mIp6; + aMapping.mCounters = mCounters; + + // We are removing expired mappings lazily, and an expired mapping might become active again before actually + // removed. Report the mapping to be "just expired" to avoid confusion. + if (mExpiry < aNow) + { + aMapping.mRemainingTimeMs = 0; + } + else + { + aMapping.mRemainingTimeMs = mExpiry - aNow; + } +} + void Translator::ReleaseMapping(AddressMapping &aMapping) { IgnoreError(mIp4AddressPool.PushBack(aMapping.mIp4)); @@ -300,6 +342,7 @@ Translator::AddressMapping *Translator::AllocateMapping(const Ip6::Address &aIp6 VerifyOrExit(mapping != nullptr); mActiveAddressMappings.Push(*mapping); + mapping->mId = ++mNextMappingId; mapping->mIp6 = aIp6Addr; // PopBack must return a valid address since it is not empty. mapping->mIp4 = *mIp4AddressPool.PopBack(); @@ -460,6 +503,93 @@ void Translator::MappingExpirerHandler(Timer &aTimer) aTimer.Get().mMappingExpirer.Start(kAddressMappingIdleTimeoutMsec); } +void Translator::InitAddressMappingIterator(AddressMappingIterator &aIterator) +{ + aIterator.mPtr = mActiveAddressMappings.GetHead(); +} + +Error Translator::GetNextAddressMapping(AddressMappingIterator &aIterator, otNat64AddressMapping &aMapping) +{ + Error err = kErrorNotFound; + TimeMilli now = TimerMilli::GetNow(); + AddressMapping *item = static_cast(aIterator.mPtr); + + VerifyOrExit(item != nullptr); + + item->CopyTo(aMapping, now); + aIterator.mPtr = item->GetNext(); + err = kErrorNone; + +exit: + return err; +} + +Error Translator::GetIp4Cidr(Ip4::Cidr &aCidr) +{ + Error err = kErrorNone; + + VerifyOrExit(mIp4Cidr.mLength > 0, err = kErrorNotFound); + aCidr = mIp4Cidr; + +exit: + return err; +} + +Error Translator::GetIp6Prefix(Ip6::Prefix &aPrefix) +{ + Error err = kErrorNone; + + VerifyOrExit(mNat64Prefix.mLength > 0, err = kErrorNotFound); + aPrefix = mNat64Prefix; + +exit: + return err; +} + +void Translator::ProtocolCounters::Count6To4Packet(uint8_t aProtocol, uint64_t aPacketSize) +{ + switch (aProtocol) + { + case Ip6::kProtoUdp: + mUdp.m6To4Packets++; + mUdp.m6To4Bytes += aPacketSize; + break; + case Ip6::kProtoTcp: + mTcp.m6To4Packets++; + mTcp.m6To4Bytes += aPacketSize; + break; + case Ip6::kProtoIcmp6: + mIcmp.m6To4Packets++; + mIcmp.m6To4Bytes += aPacketSize; + break; + } + + mTotal.m6To4Packets++; + mTotal.m6To4Bytes += aPacketSize; +} + +void Translator::ProtocolCounters::Count4To6Packet(uint8_t aProtocol, uint64_t aPacketSize) +{ + switch (aProtocol) + { + case Ip4::kProtoUdp: + mUdp.m4To6Packets++; + mUdp.m4To6Bytes += aPacketSize; + break; + case Ip4::kProtoTcp: + mTcp.m4To6Packets++; + mTcp.m4To6Bytes += aPacketSize; + break; + case Ip4::kProtoIcmp: + mIcmp.m4To6Packets++; + mIcmp.m4To6Bytes += aPacketSize; + break; + } + + mTotal.m4To6Packets++; + mTotal.m4To6Bytes += aPacketSize; +} + } // namespace Nat64 } // namespace ot diff --git a/src/core/net/nat64_translator.hpp b/src/core/net/nat64_translator.hpp index e10548361a2..2df74e57600 100644 --- a/src/core/net/nat64_translator.hpp +++ b/src/core/net/nat64_translator.hpp @@ -61,6 +61,8 @@ class Translator : public InstanceLocator, private NonCopyable OPENTHREAD_CONFIG_NAT64_IDLE_TIMEOUT_SECONDS * Time::kOneSecondInMsec; static constexpr uint32_t kAddressMappingPoolSize = OPENTHREAD_CONFIG_NAT64_MAX_MAPPINGS; + typedef otNat64AddressMappingIterator AddressMappingIterator; ///< Address mapping Iterator. + /** * The possible results of NAT64 translation. * @@ -74,6 +76,64 @@ class Translator : public InstanceLocator, private NonCopyable kDrop, ///< The caller should drop the datagram silently. }; + /** + * Represents the counters for the protocols supported by NAT64. + * + */ + class ProtocolCounters : public otNat64ProtocolCounters, public Clearable + { + public: + /** + * Adds the packet to the counter for the given IPv6 protocol. + * + * @param[in] aProtocol The protocol of the packet. + * @param[in] aPacketSize The size of the packet. + * + */ + void Count6To4Packet(uint8_t aProtocol, uint64_t aPacketSize); + + /** + * Adds the packet to the counter for the given IPv4 protocol. + * + * @param[in] aProtocol The protocol of the packet. + * @param[in] aPacketSize The size of the packet. + * + */ + void Count4To6Packet(uint8_t aProtocol, uint64_t aPacketSize); + }; + + /** + * Represents the counters of dropped packets due to errors when handling NAT64 packets. + * + */ + class ErrorCounters : public otNat64ErrorCounters, public Clearable + { + public: + enum Reason : uint8_t + { + kUnknown = OT_NAT64_DROP_REASON_UNKNOWN, + kIllegalPacket = OT_NAT64_DROP_REASON_ILLEGAL_PACKET, + kUnsupportedProto = OT_NAT64_DROP_REASON_UNSUPPORTED_PROTO, + kNoMapping = OT_NAT64_DROP_REASON_NO_MAPPING, + }; + + /** + * Adds the counter for the given reason when translating an IPv4 datagram. + * + * @param[in] aReason The reason of packet drop. + * + */ + void Count4To6(Reason aReason) { mCount4To6[aReason]++; } + + /** + * Adds the counter for the given reason when translating an IPv6 datagram. + * + * @param[in] aReason The reason of packet drop. + * + */ + void Count6To4(Reason aReason) { mCount6To4[aReason]++; } + }; + /** * This constructor initializes the NAT64 translator. * @@ -163,6 +223,76 @@ class Translator : public InstanceLocator, private NonCopyable */ void SetNat64Prefix(const Ip6::Prefix &aNat64Prefix); + /** + * Initializes an `otNat64AddressMappingIterator`. + * + * An iterator MUST be initialized before it is used. + * + * An iterator can be initialized again to restart from the beginning of the mapping info. + * + * @param[out] aIterator An iterator to initialize. + * + */ + void InitAddressMappingIterator(AddressMappingIterator &aIterator); + + /** + * Gets the next AddressMapping info (using an iterator). + * + * @param[in,out] aIterator The iterator. On success the iterator will be updated to point to next NAT64 + * address mapping record. To get the first entry the iterator should be set to + * OT_NAT64_ADDRESS_MAPPING_ITERATOR_INIT. + * @param[out] aMapping An `otNat64AddressMapping` where information of next NAT64 address mapping record + * is placed (on success). + * + * @retval kErrorNone Successfully found the next NAT64 address mapping info (@p aMapping was successfully + * updated). + * @retval kErrorNotFound No subsequent NAT64 address mapping info was found. + * + */ + Error GetNextAddressMapping(AddressMappingIterator &aIterator, otNat64AddressMapping &aMapping); + + /** + * Gets the NAT64 translator counters. + * + * The counters are initialized to zero when the OpenThread instance is initialized. + * + * @param[out] aCounters A `ProtocolCounters` where the counters of NAT64 translator will be placed. + * + */ + void GetCounters(ProtocolCounters &aCounters) const { aCounters = mCounters; } + + /** + * Gets the NAT64 translator error counters. + * + * The counters are initialized to zero when the OpenThread instance is initialized. + * + * @param[out] aCounters An `ErrorCounters` where the counters of NAT64 translator will be placed. + * + */ + void GetErrorCounters(ErrorCounters &aCounters) const { aCounters = mErrorCounters; } + + /** + * Gets the configured CIDR in the NAT64 translator. + * + * @param[out] aCidr The `Ip4::Cidr` Where the configured CIDR will be placed. + * + * @retval kErrorNone @p aCidr is set to the configured CIDR. + * @retval kErrorNotFound The translator is not configured with an IPv4 CIDR. + * + */ + Error GetIp4Cidr(Ip4::Cidr &aCidr); + + /** + * Gets the configured IPv6 prefix in the NAT64 translator. + * + * @param[out] aPrefix The `Ip6::Prefix` where the configured NAT64 prefix will be placed. + * + * @retval kErrorNone @p aPrefix is set to the configured prefix. + * @retval kErrorNotFound The translator is not configured with an IPv6 prefix. + * + */ + Error GetIp6Prefix(Ip6::Prefix &aPrefix); + private: class AddressMapping : public LinkedListEntry { @@ -174,11 +304,16 @@ class Translator : public InstanceLocator, private NonCopyable void Touch(TimeMilli aNow) { mExpiry = aNow + kAddressMappingIdleTimeoutMsec; } InfoString ToString(void); + void CopyTo(otNat64AddressMapping &aMapping, TimeMilli aNow) const; + + uint64_t mId; // The unique id for a mapping session. Ip4::Address mIp4; Ip6::Address mIp6; TimeMilli mExpiry; // The timestamp when this mapping expires, in milliseconds. + ProtocolCounters mCounters; + private: bool Matches(const Ip4::Address &aIp4) const { return mIp4 == aIp4; } bool Matches(const Ip6::Address &aIp6) const { return mIp6 == aIp6; } @@ -198,6 +333,8 @@ class Translator : public InstanceLocator, private NonCopyable static void MappingExpirerHandler(Timer &aTimer); + uint64_t mNextMappingId; + Array mIp4AddressPool; Pool mAddressMappingPool; LinkedList mActiveAddressMappings; @@ -206,9 +343,16 @@ class Translator : public InstanceLocator, private NonCopyable Ip4::Cidr mIp4Cidr; TimerMilli mMappingExpirer; + + ProtocolCounters mCounters; + ErrorCounters mErrorCounters; }; } // namespace Nat64 + +DefineCoreType(otNat64ProtocolCounters, Nat64::Translator::ProtocolCounters); +DefineCoreType(otNat64ErrorCounters, Nat64::Translator::ErrorCounters); + } // namespace ot #endif // OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE diff --git a/src/core/net/srp_server.cpp b/src/core/net/srp_server.cpp index a065880cea0..d04d1f7d0b8 100644 --- a/src/core/net/srp_server.cpp +++ b/src/core/net/srp_server.cpp @@ -96,6 +96,9 @@ Server::Server(Instance &aInstance) , mAddressMode(kDefaultAddressMode) , mAnycastSequenceNumber(0) , mHasRegisteredAnyService(false) +#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE + , mAutoEnable(false) +#endif { IgnoreError(SetDomain(kDefaultDomain)); } @@ -134,41 +137,71 @@ Error Server::SetAnycastModeSequenceNumber(uint8_t aSequenceNumber) void Server::SetEnabled(bool aEnabled) { +#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE + mAutoEnable = false; +#endif + if (aEnabled) { - VerifyOrExit(mState == kStateDisabled); - mState = kStateStopped; + Enable(); + } + else + { + Disable(); + } +} - // Request publishing of "DNS/SRP Address Service" entry in the - // Thread Network Data based of `mAddressMode`. Then wait for - // callback `HandleNetDataPublisherEntryChange()` from the - // `Publisher` to start the SRP server. +void Server::Enable(void) +{ + VerifyOrExit(mState == kStateDisabled); + mState = kStateStopped; - switch (mAddressMode) - { - case kAddressModeUnicast: - SelectPort(); - Get().PublishDnsSrpServiceUnicast(mPort); - break; + // Request publishing of "DNS/SRP Address Service" entry in the + // Thread Network Data based of `mAddressMode`. Then wait for + // callback `HandleNetDataPublisherEntryChange()` from the + // `Publisher` to start the SRP server. - case kAddressModeAnycast: - mPort = kAnycastAddressModePort; - Get().PublishDnsSrpServiceAnycast(mAnycastSequenceNumber); - break; - } - } - else + switch (mAddressMode) { - VerifyOrExit(mState != kStateDisabled); - Get().UnpublishDnsSrpService(); - Stop(); - mState = kStateDisabled; + case kAddressModeUnicast: + SelectPort(); + Get().PublishDnsSrpServiceUnicast(mPort); + break; + + case kAddressModeAnycast: + mPort = kAnycastAddressModePort; + Get().PublishDnsSrpServiceAnycast(mAnycastSequenceNumber); + break; } exit: return; } +void Server::Disable(void) +{ + VerifyOrExit(mState != kStateDisabled); + Get().UnpublishDnsSrpService(); + Stop(); + mState = kStateDisabled; + +exit: + return; +} + +#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE +void Server::SetAutoEnableMode(bool aEnabled) +{ + VerifyOrExit(mAutoEnable != aEnabled); + mAutoEnable = aEnabled; + + Get().HandleSrpServerAutoEnableMode(); + +exit: + return; +} +#endif + Server::TtlConfig::TtlConfig(void) { mMinTtl = kDefaultMinTtl; @@ -1296,6 +1329,48 @@ void Server::HandleUpdate(Host &aHost, const MessageMetadata &aMetadata) void Server::InformUpdateHandlerOrCommit(Error aError, Host &aHost, const MessageMetadata &aMetadata) { +#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO) + if (aError == kErrorNone) + { + uint8_t numAddrs; + const Ip6::Address *addrs; + + LogInfo("Processed DNS update info"); + LogInfo(" Host:%s", aHost.GetFullName()); + LogInfo(" Lease:%u, key-lease:%u, ttl:%u", aHost.GetLease(), aHost.GetKeyLease(), aHost.GetTtl()); + + addrs = aHost.GetAddresses(numAddrs); + + if (numAddrs == 0) + { + LogInfo(" No host address"); + } + else + { + LogInfo(" %d host address(es):", numAddrs); + + for (; numAddrs > 0; addrs++, numAddrs--) + { + LogInfo(" %s", addrs->ToString().AsCString()); + } + } + + for (const Service &service : aHost.GetServices()) + { + char subLabel[Dns::Name::kMaxLabelSize]; + + IgnoreError(service.GetServiceSubTypeLabel(subLabel, sizeof(subLabel))); + + LogInfo(" %s service '%s'%s%s", service.IsDeleted() ? "Deleting" : "Adding", service.GetInstanceName(), + service.IsSubType() ? " subtype:" : "", subLabel); + } + } + else + { + LogInfo("Error %s processing received DNS update", ErrorToString(aError)); + } +#endif // OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO) + if ((aError == kErrorNone) && (mServiceUpdateHandler != nullptr)) { UpdateMetadata *update = UpdateMetadata::Allocate(GetInstance(), aHost, aMetadata); @@ -1740,7 +1815,7 @@ void Server::Service::Log(Action aAction) const "Update existing", // (1) kUpdateExisting "Remove but retain name of", // (2) kRemoveButRetainName "Fully remove", // (3) kFullyRemove - "LEASE expired for ", // (4) kLeaseExpired + "LEASE expired for", // (4) kLeaseExpired "KEY LEASE expired for", // (5) kKeyLeaseExpired }; diff --git a/src/core/net/srp_server.hpp b/src/core/net/srp_server.hpp index 302ccc6cb96..dbf6090763a 100644 --- a/src/core/net/srp_server.hpp +++ b/src/core/net/srp_server.hpp @@ -92,6 +92,12 @@ class Server; } } // namespace Dns +#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE +namespace BorderRouter { +class RoutingManager; +} +#endif + namespace Srp { /** @@ -105,6 +111,9 @@ class Server : public InstanceLocator, private NonCopyable friend class Service; friend class Host; friend class Dns::ServiceDiscovery::Server; +#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE + friend class BorderRouter::RoutingManager; +#endif enum RetainName : bool { @@ -151,11 +160,15 @@ class Server : public InstanceLocator, private NonCopyable class Host; + /** + * This enumeration represents the state of SRP server. + * + */ enum State : uint8_t { - kStateDisabled = OT_SRP_SERVER_STATE_DISABLED, - kStateRunning = OT_SRP_SERVER_STATE_RUNNING, - kStateStopped = OT_SRP_SERVER_STATE_STOPPED, + kStateDisabled = OT_SRP_SERVER_STATE_DISABLED, ///< Server is disabled. + kStateRunning = OT_SRP_SERVER_STATE_RUNNING, ///< Server is enabled and running. + kStateStopped = OT_SRP_SERVER_STATE_STOPPED, ///< Server is enabled but stopped. }; /** @@ -775,17 +788,9 @@ class Server : public InstanceLocator, private NonCopyable Error SetAnycastModeSequenceNumber(uint8_t aSequenceNumber); /** - * This method tells whether the SRP server is currently running. + * This method returns the state of the SRP server. * - * @returns A boolean that indicates whether the server is running. - * - */ - bool IsRunning(void) const { return (mState == kStateRunning); } - - /** - * This method tells the state of the SRP server. - * - * @returns An enum that represents the state of the server. + * @returns The state of the server. * */ State GetState(void) const { return mState; } @@ -793,10 +798,10 @@ class Server : public InstanceLocator, private NonCopyable /** * This method tells the port the SRP server is listening to. * - * @returns An integer that represents the port of the server. It returns 0 if the SRP server is not running. + * @returns The port of the server or 0 if the SRP server is not running. * */ - uint16_t GetPort(void) const { return IsRunning() ? mPort : 0; } + uint16_t GetPort(void) const { return (mState == kStateRunning) ? mPort : 0; } /** * This method enables/disables the SRP server. @@ -806,6 +811,35 @@ class Server : public InstanceLocator, private NonCopyable */ void SetEnabled(bool aEnabled); +#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE + /** + * This method enables/disables the auto-enable mode on SRP server. + * + * When this mode is enabled, the Border Routing Manager controls if/when to enable or disable the SRP server. + * SRP sever is auto-enabled if/when Border Routing is started it is done with the initial prefix and route + * configurations (when the OMR and on-link prefixes are determined, advertised in emitted Router Advert message on + * infrastructure side and published in the Thread Network Data). The SRP server is auto-disabled when BR is + * stopped (e.g., if the infrastructure network interface is brought down or if BR gets detached). + * + * This mode can be disabled by a `SetAutoEnableMode(false)` call or if the SRP server is explicitly enabled or + * disabled by a call to `SetEnabled()` method. Disabling auto-enable mode using `SetAutoEnableMode(false` call + * will not change the current state of SRP sever (e.g., if it is enabled it stays enabled). + * + * @param[in] aEnbaled A boolean to enable/disable the auto-enable mode. + * + */ + void SetAutoEnableMode(bool aEnabled); + + /** + * This method indicates whether the auto-enable mode is enabled or disabled. + * + * @retval TRUE The auto-enable mode is enabled. + * @retval FALSE The auto-enable mode is disabled. + * + */ + bool IsAutoEnableMode(void) const { return mAutoEnable; } +#endif + /** * This method returns the TTL configuration. * @@ -942,6 +976,8 @@ class Server : public InstanceLocator, private NonCopyable bool mIsDirectRxFromClient; }; + void Enable(void); + void Disable(void); void Start(void); void Stop(void); void SelectPort(void); @@ -1039,6 +1075,9 @@ class Server : public InstanceLocator, private NonCopyable AddressMode mAddressMode; uint8_t mAnycastSequenceNumber; bool mHasRegisteredAnyService : 1; +#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE + bool mAutoEnable : 1; +#endif otSrpServerResponseCounters mResponseCounters; }; diff --git a/src/core/net/tcp6.hpp b/src/core/net/tcp6.hpp index 3eb6d134f37..febefe9c113 100644 --- a/src/core/net/tcp6.hpp +++ b/src/core/net/tcp6.hpp @@ -55,7 +55,16 @@ extern "C" { struct tcpcb; struct tcpcb_listen; struct tcplp_signals; + +/* + * The next two declarations intentionally change argument names from the + * original declarations in TCPlp, in order to comply with OpenThread's format. + */ + +// NOLINTNEXTLINE(readability-inconsistent-declaration-parameter-name) void tcplp_sys_set_timer(struct tcpcb *aTcb, uint8_t aTimerFlag, uint32_t aDelay); + +// NOLINTNEXTLINE(readability-inconsistent-declaration-parameter-name) void tcplp_sys_stop_timer(struct tcpcb *aTcb, uint8_t aTimerFlag); } diff --git a/src/core/net/tcp6_ext.cpp b/src/core/net/tcp6_ext.cpp new file mode 100644 index 00000000000..c789d45b474 --- /dev/null +++ b/src/core/net/tcp6_ext.cpp @@ -0,0 +1,231 @@ +/* + * Copyright (c) 2022, The OpenThread Authors. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file + * This file implements TCP/IPv6 socket extensions. + */ + +#include "openthread-core-config.h" + +#if OPENTHREAD_CONFIG_TCP_ENABLE + +#include "tcp6_ext.hpp" + +#include "common/code_utils.hpp" +#include "common/error.hpp" +#include "common/locator_getters.hpp" +#include "common/log.hpp" + +namespace ot { +namespace Ip6 { + +RegisterLogModule("TcpExt"); + +void TcpCircularSendBuffer::Initialize(void *aDataBuffer, size_t aCapacity) +{ + mDataBuffer = static_cast(aDataBuffer); + mCapacity = aCapacity; + ForceDiscardAll(); +} + +Error TcpCircularSendBuffer::Write(Tcp::Endpoint &aEndpoint, + const void * aData, + size_t aLength, + size_t & aWritten, + uint32_t aFlags) +{ + Error error = kErrorNone; + size_t bytesFree = GetFreeSpace(); + size_t writeIndex; + uint32_t flags = 0; + size_t bytesUntilWrap; + + /* + * Handle the case where we don't have enough space to accommodate all of the + * provided data. + */ + aLength = Min(aLength, bytesFree); + VerifyOrExit(aLength != 0); + + /* + * This is a "simplifying" if statement the removes an edge case from the logic + * below. It guarantees that a write to an empty buffer will never wrap. + */ + if (mCapacityUsed == 0) + { + mStartIndex = 0; + } + + writeIndex = GetIndex(mStartIndex, mCapacityUsed); + + if ((aFlags & OT_TCP_CIRCULAR_SEND_BUFFER_WRITE_MORE_TO_COME) != 0 && aLength < bytesFree) + { + flags |= OT_TCP_SEND_MORE_TO_COME; + } + + bytesUntilWrap = mCapacity - writeIndex; + if (aLength <= bytesUntilWrap) + { + memcpy(&mDataBuffer[writeIndex], aData, aLength); + if (writeIndex == 0) + { + /* + * mCapacityUsed == 0 corresponds to the case where we're writing + * to an empty buffer. mCapacityUsed != 0 && writeIndex == 0 + * corresponds to the case where the buffer is not empty and this is + * writing the first bytes that wrap. + */ + uint8_t linkIndex; + if (mCapacityUsed == 0) + { + linkIndex = mFirstSendLinkIndex; + } + else + { + linkIndex = 1 - mFirstSendLinkIndex; + } + { + otLinkedBuffer &dataSendLink = mSendLinks[linkIndex]; + + dataSendLink.mNext = nullptr; + dataSendLink.mData = &mDataBuffer[writeIndex]; + dataSendLink.mLength = aLength; + + LogDebg("Appending link %u (points to index %u, length %u)", static_cast(linkIndex), + static_cast(writeIndex), static_cast(aLength)); + error = aEndpoint.SendByReference(dataSendLink, flags); + } + } + else + { + LogDebg("Extending tail link by length %u", static_cast(aLength)); + error = aEndpoint.SendByExtension(aLength, flags); + } + VerifyOrExit(error == kErrorNone, aLength = 0); + } + else + { + const uint8_t *dataIndexable = static_cast(aData); + size_t bytesWrapped = aLength - bytesUntilWrap; + + memcpy(&mDataBuffer[writeIndex], &dataIndexable[0], bytesUntilWrap); + memcpy(&mDataBuffer[0], &dataIndexable[bytesUntilWrap], bytesWrapped); + + /* + * Because of the "simplifying" if statement at the top, we don't + * have to worry about starting from an empty buffer in this case. + */ + LogDebg("Extending tail link by length %u (wrapping)", static_cast(bytesUntilWrap)); + error = aEndpoint.SendByExtension(bytesUntilWrap, flags | OT_TCP_SEND_MORE_TO_COME); + VerifyOrExit(error == kErrorNone, aLength = 0); + + { + otLinkedBuffer &wrappedDataSendLink = mSendLinks[1 - mFirstSendLinkIndex]; + + wrappedDataSendLink.mNext = nullptr; + wrappedDataSendLink.mData = &mDataBuffer[0]; + wrappedDataSendLink.mLength = bytesWrapped; + + LogDebg("Appending link %u (wrapping)", static_cast(1 - mFirstSendLinkIndex)); + error = aEndpoint.SendByReference(wrappedDataSendLink, flags); + VerifyOrExit(error == kErrorNone, aLength = bytesUntilWrap); + } + } + +exit: + mCapacityUsed += aLength; + aWritten = aLength; + return error; +} + +void TcpCircularSendBuffer::HandleForwardProgress(size_t aInSendBuffer) +{ + size_t bytesRemoved; + size_t bytesUntilWrap; + + OT_ASSERT(aInSendBuffer <= mCapacityUsed); + LogDebg("Forward progress: %u bytes in send buffer\n", aInSendBuffer); + bytesRemoved = mCapacityUsed - aInSendBuffer; + bytesUntilWrap = mCapacity - mStartIndex; + + if (bytesRemoved < bytesUntilWrap) + { + mStartIndex += bytesRemoved; + } + else + { + mStartIndex = bytesRemoved - bytesUntilWrap; + /* The otLinkedBuffer for the pre-wrap data is now empty. */ + LogDebg("Pre-wrap linked buffer now empty: switching first link index from %u to %u\n", + static_cast(mFirstSendLinkIndex), static_cast(1 - mFirstSendLinkIndex)); + mFirstSendLinkIndex = 1 - mFirstSendLinkIndex; + } + mCapacityUsed = aInSendBuffer; +} + +size_t TcpCircularSendBuffer::GetFreeSpace(void) const +{ + return mCapacity - mCapacityUsed; +} + +void TcpCircularSendBuffer::ForceDiscardAll(void) +{ + mStartIndex = 0; + mCapacityUsed = 0; + mFirstSendLinkIndex = 0; +} + +Error TcpCircularSendBuffer::Deinitialize(void) +{ + return (mCapacityUsed != 0) ? kErrorBusy : kErrorNone; +} + +size_t TcpCircularSendBuffer::GetIndex(size_t aStart, size_t aOffsetFromStart) const +{ + size_t bytesUntilWrap; + size_t index; + + OT_ASSERT(aStart < mCapacity); + bytesUntilWrap = mCapacity - aStart; + if (aOffsetFromStart < bytesUntilWrap) + { + index = aStart + aOffsetFromStart; + } + else + { + index = aOffsetFromStart - bytesUntilWrap; + } + + return index; +} + +} // namespace Ip6 +} // namespace ot + +#endif // OPENTHREAD_CONFIG_TCP_ENABLE diff --git a/src/core/net/tcp6_ext.hpp b/src/core/net/tcp6_ext.hpp new file mode 100644 index 00000000000..b40663aedb2 --- /dev/null +++ b/src/core/net/tcp6_ext.hpp @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2022, The OpenThread Authors. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file + * This file includes definitions for TCP/IPv6 socket extensions. + */ + +#ifndef TCP6_EXT_HPP_ +#define TCP6_EXT_HPP_ + +#include "openthread-core-config.h" + +#include + +#include "net/tcp6.hpp" + +namespace ot { +namespace Ip6 { + +/** + * @addtogroup core-tcp-ext + * + * @brief + * This module includes definitions for TCP/IPv6 socket extensions. + * + * @{ + * + */ + +/** + * This class represents a TCP circular send buffer. + * + */ +class TcpCircularSendBuffer : public otTcpCircularSendBuffer +{ +public: + /** + * Initializes a TCP circular send buffer. + * + * @sa otTcpCircularSendBufferInitialize + * + * @param[in] aDataBuffer A pointer to memory to use to store data in the TCP circular send buffer. + * @param[in] aCapacity The capacity, in bytes, of the TCP circular send buffer, which must equal the size + * of the memory pointed to by @p aDataBuffer . + */ + void Initialize(void *aDataBuffer, size_t aCapacity); + + /** + * Sends out data on a TCP endpoint, using this TCP circular send buffer to manage buffering. + * + * @sa otTcpCircularSendBufferWrite, particularly for guidance on how @p aEndpoint must be chosen. + * + * @param[in] aEndpoint The TCP endpoint on which to send out data. + * @param[in] aData A pointer to data to copy into the TCP circular send buffer. + * @param[in] aLength The length of the data pointed to by @p aData to copy into the TCP circular send buffer. + * @param[out] aWritten Populated with the amount of data copied into the send buffer, which might be less than + * @p aLength if the send buffer reaches capacity. + * @param[in] aFlags Flags specifying options for this operation. + * + * @retval kErrorNone on success, or the error returned by the TCP endpoint on failure. + */ + Error Write(Tcp::Endpoint &aEndpoint, const void *aData, size_t aLength, size_t &aWritten, uint32_t aFlags); + + /** + * Performs circular-send-buffer-specific handling in the otTcpForwardProgress callback. + * + * @sa otTcpCircularSendBufferHandleForwardProgress + * + * @param[in] aInSendBuffer Value of @p aInSendBuffer passed to the otTcpForwardProgress() callback. + */ + void HandleForwardProgress(size_t aInSendBuffer); + + /** + * Returns the amount of free space in this TCP circular send buffer. + * + * @sa otTcpCircularSendBufferFreeSpace + * + * @return The amount of free space in the send buffer. + */ + size_t GetFreeSpace(void) const; + + /** + * Forcibly discards all data in this TCP circular send buffer. + * + * @sa otTcpCircularSendBufferForceDiscardAll + * + */ + void ForceDiscardAll(void); + + /** + * Deinitializes this TCP circular send buffer. + * + * @sa otTcpCircularSendBufferDeinitialize + * + * @retval kErrorNone Successfully deinitialized this TCP circular send buffer. + * @retval kErrorFailed Failed to deinitialize the TCP circular send buffer. + */ + Error Deinitialize(void); + +private: + size_t GetIndex(size_t aStart, size_t aOffsetFromStart) const; +}; + +} // namespace Ip6 + +DefineCoreType(otTcpCircularSendBuffer, Ip6::TcpCircularSendBuffer); + +} // namespace ot + +#endif // TCP6_HPP_ diff --git a/src/core/thread/link_metrics.cpp b/src/core/thread/link_metrics.cpp index 024bbab5331..6ecf26d1e45 100644 --- a/src/core/thread/link_metrics.cpp +++ b/src/core/thread/link_metrics.cpp @@ -64,25 +64,29 @@ LinkMetrics::LinkMetrics(Instance &aInstance) Error LinkMetrics::Query(const Ip6::Address &aDestination, uint8_t aSeriesId, const Metrics *aMetrics) { - Error error; - TypeIdFlags typeIdFlags[kMaxTypeIdFlags]; - uint8_t typeIdFlagsCount = 0; - Neighbor * neighbor = GetNeighborFromLinkLocalAddr(aDestination); + static const uint8_t kTlvs[] = {Mle::Tlv::kLinkMetricsReport}; + + Error error; + Neighbor *neighbor = GetNeighborFromLinkLocalAddr(aDestination); + QueryInfo info; VerifyOrExit(neighbor != nullptr, error = kErrorUnknownNeighbor); VerifyOrExit(neighbor->IsThreadVersion1p2OrHigher(), error = kErrorNotCapable); + info.Clear(); + info.mSeriesId = aSeriesId; + if (aMetrics != nullptr) { - typeIdFlagsCount = TypeIdFlagsFromMetrics(typeIdFlags, *aMetrics); + info.mTypeIdCount = TypeIdFlagsFromMetrics(info.mTypeIds, *aMetrics); } if (aSeriesId != 0) { - VerifyOrExit(typeIdFlagsCount == 0, error = kErrorInvalidArgs); + VerifyOrExit(info.mTypeIdCount == 0, error = kErrorInvalidArgs); } - error = SendLinkMetricsQuery(aDestination, aSeriesId, typeIdFlags, typeIdFlagsCount); + error = Get().SendDataRequest(aDestination, kTlvs, sizeof(kTlvs), /* aDelay */ 0, info); exit: return error; @@ -560,51 +564,31 @@ void LinkMetrics::ProcessEnhAckIeData(const uint8_t *aData, uint8_t aLength, con return; } -Error LinkMetrics::SendLinkMetricsQuery(const Ip6::Address &aDestination, - uint8_t aSeriesId, - const TypeIdFlags * aTypeIdFlags, - uint8_t aTypeIdFlagsCount) +Error LinkMetrics::AppendLinkMetricsQueryTlv(Message &aMessage, const QueryInfo &aInfo) { - // LinkMetricsQuery Tlv + LinkMetricsQueryId sub-TLV (value-length: 1 byte) + - // LinkMetricsQueryOptions sub-TLV (value-length: `kMaxTypeIdFlags` bytes) - constexpr uint16_t kBufferSize = sizeof(Tlv) * 3 + sizeof(uint8_t) + sizeof(TypeIdFlags) * kMaxTypeIdFlags; - - Error error = kErrorNone; - QueryOptionsSubTlv queryOptionsTlv; - uint8_t length = 0; - static const uint8_t tlvs[] = {Mle::Tlv::kLinkMetricsReport}; - uint8_t buf[kBufferSize]; - Tlv * tlv = reinterpret_cast(buf); - Tlv subTlv; - - // Link Metrics Query TLV - tlv->SetType(Mle::Tlv::kLinkMetricsQuery); - length += sizeof(Tlv); - - // Link Metrics Query ID sub-TLV - subTlv.SetType(SubTlv::kQueryId); - subTlv.SetLength(sizeof(uint8_t)); - memcpy(buf + length, &subTlv, sizeof(subTlv)); - length += sizeof(subTlv); - memcpy(buf + length, &aSeriesId, sizeof(aSeriesId)); - length += sizeof(aSeriesId); - - // Link Metrics Query Options sub-TLV - if (aTypeIdFlagsCount > 0) - { - queryOptionsTlv.Init(); - queryOptionsTlv.SetLength(aTypeIdFlagsCount * sizeof(TypeIdFlags)); + Error error = kErrorNone; + Tlv tlv; - memcpy(buf + length, &queryOptionsTlv, sizeof(queryOptionsTlv)); - length += sizeof(queryOptionsTlv); - memcpy(buf + length, aTypeIdFlags, queryOptionsTlv.GetLength()); - length += queryOptionsTlv.GetLength(); - } + // The MLE Link Metrics Query TLV has two sub-TLVs: + // - Query ID sub-TLV with series ID as value. + // - Query Options sub-TLV with Type IDs as value. - // Set Length for Link Metrics Report TLV - tlv->SetLength(length - sizeof(Tlv)); + tlv.SetType(Mle::Tlv::kLinkMetricsQuery); + tlv.SetLength(sizeof(Tlv) + sizeof(uint8_t) + ((aInfo.mTypeIdCount == 0) ? 0 : (sizeof(Tlv) + aInfo.mTypeIdCount))); - SuccessOrExit(error = Get().SendDataRequest(aDestination, tlvs, sizeof(tlvs), 0, buf, length)); + SuccessOrExit(error = aMessage.Append(tlv)); + + SuccessOrExit(error = Tlv::Append(aMessage, aInfo.mSeriesId)); + + if (aInfo.mTypeIdCount != 0) + { + QueryOptionsSubTlv queryOptionsTlv; + + queryOptionsTlv.Init(); + queryOptionsTlv.SetLength(aInfo.mTypeIdCount); + SuccessOrExit(error = aMessage.Append(queryOptionsTlv)); + SuccessOrExit(error = aMessage.AppendBytes(aInfo.mTypeIds, aInfo.mTypeIdCount)); + } exit: return error; diff --git a/src/core/thread/link_metrics.hpp b/src/core/thread/link_metrics.hpp index 742223853fb..702e7982ab5 100644 --- a/src/core/thread/link_metrics.hpp +++ b/src/core/thread/link_metrics.hpp @@ -84,6 +84,17 @@ class LinkMetrics : public InstanceLocator, private NonCopyable typedef otLinkMetricsMgmtResponseCallback MgmtResponseCallback; typedef otLinkMetricsEnhAckProbingIeReportCallback EnhAckProbingIeReportCallback; + /** + * This structure provides the info used for appending MLE Link Metric Query TLV. + * + */ + struct QueryInfo : public Clearable + { + uint8_t mSeriesId; ///< Series ID. + TypeIdFlags mTypeIds[kMaxTypeIdFlags]; ///< Type ID flags. + uint8_t mTypeIdCount; ///< Number of entries in `mTypeIds[]`. + }; + /** * This constructor initializes an instance of the LinkMetrics class. * @@ -264,6 +275,18 @@ class LinkMetrics : public InstanceLocator, private NonCopyable */ void ProcessEnhAckIeData(const uint8_t *aData, uint8_t aLength, const Neighbor &aNeighbor); + /** + * This method appends MLE Link Metrics Query TLV to a given message. + * + * @param[in] aMessage The message to append to. + * @param[in] aInfo The link metrics query info to use to prepare the message. + * + * @retval kErrorNone Successfully appended the TLV to the message. + * @retval kErrorNoBufs Insufficient buffers available to append the TLV. + * + */ + Error AppendLinkMetricsQueryTlv(Message &aMessage, const QueryInfo &aInfo); + private: // Max number of SeriesInfo that could be allocated by the pool. static constexpr uint16_t kMaxSeriesSupported = OPENTHREAD_CONFIG_MLE_LINK_METRICS_MAX_SERIES_SUPPORTED; @@ -277,11 +300,6 @@ class LinkMetrics : public InstanceLocator, private NonCopyable static constexpr int32_t kMinRssi = -130; static constexpr int32_t kMaxRssi = 0; - Error SendLinkMetricsQuery(const Ip6::Address &aDestination, - uint8_t aSeriesId, - const TypeIdFlags * aTypeIdFlags, - uint8_t aTypeIdFlagsCount); - Status ConfigureForwardTrackingSeries(uint8_t aSeriesId, uint8_t aSeriesFlags, const Metrics &aMetrics, diff --git a/src/core/thread/mle.cpp b/src/core/thread/mle.cpp index b277dfd10ae..286d97abc9c 100644 --- a/src/core/thread/mle.cpp +++ b/src/core/thread/mle.cpp @@ -1797,12 +1797,15 @@ Error Mle::SendChildIdRequest(void) return error; } -Error Mle::SendDataRequest(const Ip6::Address &aDestination, - const uint8_t * aTlvs, - uint8_t aTlvsLength, - uint16_t aDelay, - const uint8_t * aExtraTlvs, - uint8_t aExtraTlvsLength) +#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE || OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE +Error Mle::SendDataRequest(const Ip6::Address & aDestination, + const uint8_t * aTlvs, + uint8_t aTlvsLength, + uint16_t aDelay, + const LinkMetrics::LinkMetrics::QueryInfo *aQueryInfo) +#else +Error Mle::SendDataRequest(const Ip6::Address &aDestination, const uint8_t *aTlvs, uint8_t aTlvsLength, uint16_t aDelay) +#endif { Error error = kErrorNone; TxMessage *message; @@ -1812,10 +1815,12 @@ Error Mle::SendDataRequest(const Ip6::Address &aDestination, VerifyOrExit((message = NewMleMessage(kCommandDataRequest)) != nullptr, error = kErrorNoBufs); SuccessOrExit(error = message->AppendTlvRequestTlv(aTlvs, aTlvsLength)); - if (aExtraTlvs != nullptr && aExtraTlvsLength > 0) +#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE || OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE + if (aQueryInfo != nullptr) { - SuccessOrExit(error = message->AppendBytes(aExtraTlvs, aExtraTlvsLength)); + SuccessOrExit(error = Get().AppendLinkMetricsQueryTlv(*message, *aQueryInfo)); } +#endif if (aDelay) { diff --git a/src/core/thread/mle.hpp b/src/core/thread/mle.hpp index 961065026b7..ec4abe749a7 100644 --- a/src/core/thread/mle.hpp +++ b/src/core/thread/mle.hpp @@ -1447,26 +1447,29 @@ class Mle : public InstanceLocator, private NonCopyable */ Mac::ShortAddress GetNextHop(uint16_t aDestination) const; +#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE || OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE /** - * This method generates an MLE Data Request message. + * This method generates an MLE Data Request message which includes a Link Metrics Query TLV. * * @param[in] aDestination A reference to the IPv6 address of the destination. * @param[in] aTlvs A pointer to requested TLV types. * @param[in] aTlvsLength The number of TLV types in @p aTlvs. * @param[in] aDelay Delay in milliseconds before the Data Request message is sent. - * @param[in] aExtraTlvs A pointer to extra TLVs. - * @param[in] aExtraTlvsLength Length of extra TLVs. + * @param[in] aQueryInfo A Link Metrics query info. * * @retval kErrorNone Successfully generated an MLE Data Request message. * @retval kErrorNoBufs Insufficient buffers to generate the MLE Data Request message. * */ - Error SendDataRequest(const Ip6::Address &aDestination, - const uint8_t * aTlvs, - uint8_t aTlvsLength, - uint16_t aDelay, - const uint8_t * aExtraTlvs, - uint8_t aExtraTlvsLength); + Error SendDataRequest(const Ip6::Address & aDestination, + const uint8_t * aTlvs, + uint8_t aTlvsLength, + uint16_t aDelay, + const LinkMetrics::LinkMetrics::QueryInfo &aQueryInfo) + { + return SendDataRequest(aDestination, aTlvs, aTlvsLength, aDelay, &aQueryInfo); + } +#endif /** * This method generates an MLE Data Request message. @@ -1484,7 +1487,7 @@ class Mle : public InstanceLocator, private NonCopyable template Error SendDataRequest(const Ip6::Address &aDestination, const uint8_t (&aTlvs)[kArrayLength], uint16_t aDelay = 0) { - return SendDataRequest(aDestination, aTlvs, kArrayLength, aDelay, nullptr, 0); + return SendDataRequest(aDestination, aTlvs, kArrayLength, aDelay); } /** @@ -1888,6 +1891,16 @@ class Mle : public InstanceLocator, private NonCopyable bool IsDetachingGracefully(void) { return mDetachGracefullyTimer.IsRunning(); } Error SendChildUpdateRequest(bool aAppendChallenge, uint32_t aTimeout); +#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE || OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE + Error SendDataRequest(const Ip6::Address & aDestination, + const uint8_t * aTlvs, + uint8_t aTlvsLength, + uint16_t aDelay, + const LinkMetrics::LinkMetrics::QueryInfo *aQueryInfo = nullptr); +#else + Error SendDataRequest(const Ip6::Address &aDestination, const uint8_t *aTlvs, uint8_t aTlvsLength, uint16_t aDelay); +#endif + #if OPENTHREAD_FTD static void HandleDetachGracefullyAddressReleaseResponse(void * aContext, otMessage * aMessage, diff --git a/src/core/utils/parse_cmdline.cpp b/src/core/utils/parse_cmdline.cpp index 52f77ddedd2..2829e6d014c 100644 --- a/src/core/utils/parse_cmdline.cpp +++ b/src/core/utils/parse_cmdline.cpp @@ -264,6 +264,11 @@ Error ParseAsIp6Address(const char *aString, otIp6Address &aAddress) return (aString != nullptr) ? otIp6AddressFromString(aString, &aAddress) : kErrorInvalidArgs; } +Error ParseAsIp4Address(const char *aString, otIp4Address &aAddress) +{ + return (aString != nullptr) ? otIp4AddressFromString(aString, &aAddress) : kErrorInvalidArgs; +} + Error ParseAsIp6Prefix(const char *aString, otIp6Prefix &aPrefix) { enum : uint8_t diff --git a/src/core/utils/parse_cmdline.hpp b/src/core/utils/parse_cmdline.hpp index 6b9025f6f7c..7703ac21441 100644 --- a/src/core/utils/parse_cmdline.hpp +++ b/src/core/utils/parse_cmdline.hpp @@ -38,7 +38,9 @@ #include #include +#include #include +#include namespace ot { namespace Utils { @@ -182,6 +184,18 @@ otError ParseAsBool(const char *aString, bool &aBool); */ otError ParseAsIp6Address(const char *aString, otIp6Address &aAddress); +/** + * This function parses a string as an IPv4 address. + * + * @param[in] aString The string to parse. + * @param[out] aAddress A reference to an `otIp6Address` to output the parsed IPv6 address. + * + * @retval kErrorNone The string was parsed successfully. + * @retval kErrorInvalidArgs The string does not contain valid IPv4 address. + * + */ +otError ParseAsIp4Address(const char *aString, otIp4Address &aAddress); + /** * This function parses a string as an IPv6 prefix. * @@ -485,6 +499,20 @@ class Arg return CmdLineParser::ParseAsIp6Address(mString, aAddress); } + /** + * This method parses the argument as an IPv4 address. + * + * @param[out] aAddress A reference to an `otIp4Address` to output the parsed IPv4 address. + * + * @retval kErrorNone The argument was parsed successfully. + * @retval kErrorInvalidArgs The argument is empty or does not contain valid IPv4 address. + * + */ + otError ParseAsIp4Address(otIp4Address &aAddress) const + { + return CmdLineParser::ParseAsIp4Address(mString, aAddress); + } + /** * This method parses the argument as an IPv6 prefix. * diff --git a/src/posix/README.md b/src/posix/README.md index 9d3ff629378..d45a4a388e4 100644 --- a/src/posix/README.md +++ b/src/posix/README.md @@ -139,28 +139,6 @@ To build and program the device with RCP application, complete the following ste ./build/posix/src/posix/ot-cli 'spinel+hdlc+uart:///dev/ttyACM0?uart-baudrate=115200' ``` -### CC2538 - -#### Build - -``` -./script/cmake-build cc2538 -DOT_APP_CLI=OFF -DOT_APP_NCP=OFF -DOT_FTD=OFF -DOT_MTD=OFF -``` - -#### Flash - -```sh -arm-none-eabi-objcopy -O ihex build/cc2538/examples/apps/ncp/ot-rcp ot-rcp.bin -# see https://github.com/JelmerT/cc2538-bsl -python cc2538-bsl/cc2538-bsl.py -b 460800 -e -w -v -p /dev/ttyUSB0 ot-rcp.bin -``` - -#### Run - -```sh -./build/posix/src/posix/ot-cli 'spinel+hdlc+uart:///dev/ttyUSB0?uart-baudrate=115200' -``` - ## Daemon Mode OpenThread Posix Daemon mode uses a unix socket as input and output, so that OpenThread core can run as a service. And a client can communicate with it by connecting to the socket. The protocol is OpenThread CLI. diff --git a/src/posix/main.c b/src/posix/main.c index df014ea5fd0..d6de188a4cd 100644 --- a/src/posix/main.c +++ b/src/posix/main.c @@ -325,17 +325,19 @@ void otPlatReset(otInstance *aInstance) assert(false); } -static void ProcessNetif(void *aContext, uint8_t aArgsLength, char *aArgs[]) +static otError ProcessNetif(void *aContext, uint8_t aArgsLength, char *aArgs[]) { OT_UNUSED_VARIABLE(aContext); OT_UNUSED_VARIABLE(aArgsLength); OT_UNUSED_VARIABLE(aArgs); otCliOutputFormat("%s:%u\r\n", otSysGetThreadNetifName(), otSysGetThreadNetifIndex()); + + return OT_ERROR_NONE; } #if !OPENTHREAD_POSIX_CONFIG_DAEMON_ENABLE -static void ProcessExit(void *aContext, uint8_t aArgsLength, char *aArgs[]) +static otError ProcessExit(void *aContext, uint8_t aArgsLength, char *aArgs[]) { OT_UNUSED_VARIABLE(aContext); OT_UNUSED_VARIABLE(aArgsLength); diff --git a/tests/fuzz/ip6_send.cpp b/tests/fuzz/ip6_send.cpp index da2cfb38cec..df267fa227d 100644 --- a/tests/fuzz/ip6_send.cpp +++ b/tests/fuzz/ip6_send.cpp @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -58,6 +59,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) IgnoreError(otLinkSetPanId(instance, panId)); IgnoreError(otIp6SetEnabled(instance, true)); IgnoreError(otThreadSetEnabled(instance, true)); + otSrpServerSetEnabled(instance, true); IgnoreError(otThreadBecomeLeader(instance)); settings.mLinkSecurityEnabled = (data[0] & 0x1) != 0; diff --git a/tests/fuzz/ncp_hdlc_received.cpp b/tests/fuzz/ncp_hdlc_received.cpp index a61b0faa06b..d60155e4b7e 100644 --- a/tests/fuzz/ncp_hdlc_received.cpp +++ b/tests/fuzz/ncp_hdlc_received.cpp @@ -35,6 +35,7 @@ #include #include #include +#include #include #include #include @@ -66,6 +67,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) IgnoreError(otLinkSetPanId(instance, panId)); IgnoreError(otIp6SetEnabled(instance, true)); IgnoreError(otThreadSetEnabled(instance, true)); + otSrpServerSetEnabled(instance, true); IgnoreError(otThreadBecomeLeader(instance)); buf = static_cast(malloc(size)); diff --git a/tests/fuzz/oss-fuzz-build b/tests/fuzz/oss-fuzz-build index 31373c4155a..53e092ca92a 100755 --- a/tests/fuzz/oss-fuzz-build +++ b/tests/fuzz/oss-fuzz-build @@ -69,7 +69,7 @@ -DOT_SNTP_CLIENT=ON \ -DOT_SRP_CLIENT=ON \ -DOT_SRP_SERVER=ON \ - -DOT_THREAD_VERSION=1.2 \ + -DOT_THREAD_VERSION=1.3 \ -DOT_UPTIME=ON \ .. ninja diff --git a/tests/scripts/expect/cli-tcp.exp b/tests/scripts/expect/cli-tcp.exp index 838730d1623..46fa325c113 100755 --- a/tests/scripts/expect/cli-tcp.exp +++ b/tests/scripts/expect/cli-tcp.exp @@ -33,15 +33,26 @@ source "tests/scripts/expect/_multinode.exp" setup_two_nodes switch_node 1 -send "tcp init\n" +send "tcp init circular\n" expect_line "Done" switch_node 2 -send "tcp init\n" +send "tcp init linked\n" expect_line "Done" set addr_2 [get_ipaddr mleid] send "tcp listen :: 30000\n" expect_line "Done" +send "tcp stoplistening\n" +expect_line "Done" + +switch_node 1 +send "tcp connect $addr_2 30000\n" +expect_line "Done" +expect "TCP: Connection refused" + +switch_node 2 +send "tcp listen :: 30000\n" +expect_line "Done" switch_node 1 set addr_1 [get_ipaddr mleid] diff --git a/tests/toranj/openthread-core-toranj-config-simulation.h b/tests/toranj/openthread-core-toranj-config-simulation.h index 09d925d08b3..d0694e7da68 100644 --- a/tests/toranj/openthread-core-toranj-config-simulation.h +++ b/tests/toranj/openthread-core-toranj-config-simulation.h @@ -75,4 +75,12 @@ */ #define OPENTHREAD_CONFIG_DNS_DSO_ENABLE 1 +/** + * @def OPENTHREAD_CONFIG_HEAP_EXTERNAL_ENABLE + * + * Enable the external heap. + * + */ +#define OPENTHREAD_CONFIG_HEAP_EXTERNAL_ENABLE 1 + #endif /* OPENTHREAD_CORE_TORANJ_CONFIG_SIMULATION_H_ */ diff --git a/tests/unit/CMakeLists.txt b/tests/unit/CMakeLists.txt index 6d606ec8a30..2176d4df960 100644 --- a/tests/unit/CMakeLists.txt +++ b/tests/unit/CMakeLists.txt @@ -65,6 +65,7 @@ set(COMMON_LIBS ot-test-platform ${OT_MBEDTLS} ot-config + openthread-ftd ) add_executable(ot-test-aes @@ -937,6 +938,28 @@ target_link_libraries(ot-test-serial-number add_test(NAME ot-test-serial-number COMMAND ot-test-serial-number) +add_executable(ot-test-srp-server + test_srp_server.cpp +) + +target_include_directories(ot-test-srp-server + PRIVATE + ${COMMON_INCLUDES} +) + +target_compile_options(ot-test-srp-server + PRIVATE + ${COMMON_COMPILE_OPTIONS} +) + +target_link_libraries(ot-test-srp-server + PRIVATE + ${COMMON_LIBS} +) + +add_test(NAME ot-test-srp-server COMMAND ot-test-srp-server) + + add_executable(ot-test-string test_string.cpp ) diff --git a/tests/unit/Makefile.am b/tests/unit/Makefile.am index 3bd12609a9f..46e7b5622b0 100644 --- a/tests/unit/Makefile.am +++ b/tests/unit/Makefile.am @@ -152,6 +152,7 @@ check_PROGRAMS += \ ot-test-serial-number \ ot-test-routing-manager \ ot-test-smart-ptrs \ + ot-test-srp-server \ ot-test-string \ ot-test-timer \ $(NULL) @@ -366,6 +367,10 @@ ot_test_serial_number_LDADD = $(COMMON_LDADD) ot_test_serial_number_LIBTOOLFLAGS = $(COMMON_LIBTOOLFLAGS) ot_test_serial_number_SOURCES = $(COMMON_SOURCES) test_serial_number.cpp +ot_test_srp_server_LDADD = $(COMMON_LDADD) +ot_test_srp_server_LIBTOOLFLAGS = $(COMMON_LIBTOOLFLAGS) +ot_test_srp_server_SOURCES = $(COMMON_SOURCES) test_srp_server.cpp + ot_test_string_LDADD = $(COMMON_LDADD) ot_test_string_LIBTOOLFLAGS = $(COMMON_LIBTOOLFLAGS) ot_test_string_SOURCES = $(COMMON_SOURCES) test_string.cpp diff --git a/tests/unit/test_routing_manager.cpp b/tests/unit/test_routing_manager.cpp index 6de01a1bfbe..36da3aff59b 100644 --- a/tests/unit/test_routing_manager.cpp +++ b/tests/unit/test_routing_manager.cpp @@ -78,10 +78,13 @@ enum ExpectedPio static Ip6::Address sInfraIfAddress; -bool sRsEmitted; // Indicates if an RS message was emitted by BR. -bool sRaValidated; // Indicates if an RA was emitted by BR and successfully validated. -ExpectedPio sExpectedPio; // Expected PIO in the emitted RA by BR (MUST be seen in RA to set `sRaValidated`). -uint32_t sOnLinkLifetime; // Valid lifetime for local on-link prefix from the last processed RA. +bool sRsEmitted; // Indicates if an RS message was emitted by BR. +bool sRaValidated; // Indicates if an RA was emitted by BR and successfully validated. +ExpectedPio sExpectedPio; // Expected PIO in the emitted RA by BR (MUST be seen in RA to set `sRaValidated`). +bool sExpectOldOnLinkPio; // Expect to see old local prefix PIO +uint32_t sOnLinkLifetime; // Valid lifetime for local on-link prefix from the last processed RA. +uint32_t sOldOnLinkLifetime; // Valid lifetime of the old local prefix PIO (when `sExpectOldOnLinkPio`). +Ip6::Prefix sOldOnLinkPrefix; // The old on-link PIO prefix in last processed RA (when `sExpectOldOnLinkPio`) static constexpr uint16_t kMaxRioPrefixes = 10; @@ -208,7 +211,6 @@ otError otPlatInfraIfSendIcmp6Nd(uint32_t aInfraIfIndex, Log(" Router Advertisement message"); LogRouterAdvert(packet); ValidateRouterAdvert(packet); - sRaValidated = true; break; default: @@ -257,6 +259,8 @@ void AdvanceTime(uint32_t aDuration) void ValidateRouterAdvert(const Icmp6Packet &aPacket) { Ip6::Nd::RouterAdvertMessage raMsg(aPacket); + bool sawExpectedPio = false; + bool sawExpecteOldPio = false; VerifyOrQuit(raMsg.IsValid()); @@ -273,22 +277,39 @@ void ValidateRouterAdvert(const Icmp6Packet &aPacket) VerifyOrQuit(pio.IsValid()); pio.GetPrefix(prefix); - VerifyOrQuit(sExpectedPio != kNoPio, "Received RA contain an unexpected PIO"); - SuccessOrQuit(otBorderRoutingGetOnLinkPrefix(sInstance, &localOnLink)); - VerifyOrQuit(prefix == localOnLink); - if (sExpectedPio == kPioAdvertisingLocalOnLink) + if (prefix == localOnLink) { - VerifyOrQuit(pio.GetPreferredLifetime() > 0, "On link prefix is deprecated unexpectedly"); + switch (sExpectedPio) + { + case kNoPio: + break; + + case kPioAdvertisingLocalOnLink: + VerifyOrQuit(pio.GetPreferredLifetime() > 0, "On link prefix is deprecated unexpectedly"); + sOnLinkLifetime = pio.GetValidLifetime(); + sawExpectedPio = true; + break; + + case kPioDeprecatingLocalOnLink: + VerifyOrQuit(pio.GetPreferredLifetime() == 0, "On link prefix is not deprecated"); + sOnLinkLifetime = pio.GetValidLifetime(); + sawExpectedPio = true; + break; + } } else { - VerifyOrQuit(pio.GetPreferredLifetime() == 0, "On link prefix is not deprecated"); - } - - sOnLinkLifetime = pio.GetValidLifetime(); + VerifyOrQuit(pio.GetPreferredLifetime() == 0, "Old on link prefix is not deprecated"); + sOldOnLinkPrefix = prefix; + sOldOnLinkLifetime = pio.GetValidLifetime(); + if (sExpectOldOnLinkPio) + { + sawExpecteOldPio = true; + } + } break; } @@ -316,6 +337,25 @@ void ValidateRouterAdvert(const Icmp6Packet &aPacket) VerifyOrQuit(false, "Unexpected option type in RA msg"); } } + + if (!sRaValidated) + { + switch (sExpectedPio) + { + case kNoPio: + break; + case kPioAdvertisingLocalOnLink: + case kPioDeprecatingLocalOnLink: + VerifyOrQuit(sawExpectedPio, "Did not see on-link prefix PIO in the RA"); + } + + if (sExpectOldOnLinkPio) + { + VerifyOrQuit(sawExpecteOldPio, "Did not see old on-link prefix PIO in the RA"); + } + + sRaValidated = true; + } } void LogRouterAdvert(const Icmp6Packet &aPacket) @@ -702,6 +742,12 @@ void InitTest(void) AdvanceTime(10000); VerifyOrQuit(otThreadGetDeviceRole(sInstance) == OT_DEVICE_ROLE_LEADER); + + // Reset all test flags + sRsEmitted = false; + sRaValidated = false; + sExpectedPio = kNoPio; + sExpectedRios.Clear(); } //--------------------------------------------------------------------------------------------------------------------- @@ -1435,6 +1481,453 @@ void TestDomainPrefixAsOmr(void) } #endif // OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE +void TestExtPanIdChange(void) +{ + static constexpr uint32_t kMaxRaTxInterval = 601; // In seconds + + static const otExtendedPanId kExtPanId1 = {{0x01, 0x02, 0x03, 0x04, 0x05, 0x6, 0x7, 0x08}}; + static const otExtendedPanId kExtPanId2 = {{0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x99, 0x88}}; + + Ip6::Prefix localOnLink; + Ip6::Prefix oldLocalOnLink; + Ip6::Prefix localOmr; + Ip6::Prefix onLinkPrefix = PrefixFromString("2000:abba:baba::", 64); + Ip6::Address routerAddressA = AddressFromString("fd00::aaaa"); + uint32_t oldPrefixLifetime; + otOperationalDataset dataset; + + Log("--------------------------------------------------------------------------------------------"); + Log("TestExtPanIdChange"); + + InitTest(); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Start Routing Manager. Check emitted RS and RA messages. + + SuccessOrQuit(sInstance->Get().SetEnabled(true)); + + SuccessOrQuit(sInstance->Get().GetOnLinkPrefix(localOnLink)); + SuccessOrQuit(sInstance->Get().GetOmrPrefix(localOmr)); + + Log("Local on-link prefix is %s", localOnLink.ToString().AsCString()); + Log("Local OMR prefix is %s", localOmr.ToString().AsCString()); + + sRsEmitted = false; + sRaValidated = false; + sExpectedPio = kPioAdvertisingLocalOnLink; + sExpectedRios.Clear(); + sExpectedRios.Add(localOmr); + + AdvanceTime(30000); + + VerifyOrQuit(sRsEmitted); + VerifyOrQuit(sRaValidated); + VerifyOrQuit(sExpectedRios.SawAll()); + Log("Local on-link prefix is being advertised, lifetime: %d", sOnLinkLifetime); + + //= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = + // Check behavior when ext PAN ID changes while the local on-link is + // being advertised. + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Change the extended PAN ID. + + Log("Changing ext PAN ID"); + + oldLocalOnLink = localOnLink; + oldPrefixLifetime = sOnLinkLifetime; + + sRaValidated = false; + sExpectOldOnLinkPio = true; + sExpectedPio = kPioAdvertisingLocalOnLink; + + SuccessOrQuit(otDatasetGetActive(sInstance, &dataset)); + + VerifyOrQuit(dataset.mComponents.mIsExtendedPanIdPresent); + + dataset.mExtendedPanId = kExtPanId1; + SuccessOrQuit(otDatasetSetActive(sInstance, &dataset)); + + AdvanceTime(500); + SuccessOrQuit(sInstance->Get().GetOnLinkPrefix(localOnLink)); + Log("Local on-link prefix changed to %s from %s", localOnLink.ToString().AsCString(), + oldLocalOnLink.ToString().AsCString()); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Validate the received RA message and that it contains the + // old on-link prefix being deprecated. + + AdvanceTime(30000); + + VerifyOrQuit(sRaValidated); + VerifyOrQuit(sOldOnLinkPrefix == oldLocalOnLink); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Validate the Network Data to contain both the current and old + // local on-link prefixes. + + VerifyOmrPrefixInNetData(localOmr); + VerifyExternalRoutesInNetData({ExternalRoute(localOnLink, NetworkData::kRoutePreferenceMedium), + ExternalRoute(oldLocalOnLink, NetworkData::kRoutePreferenceMedium)}); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Wait for old local on-link prefix to expire. + + while (oldPrefixLifetime > kMaxRaTxInterval) + { + // Ensure Network Data entries remain as before. Mainly we still + // see the deprecating local on-link prefix. + + VerifyExternalRoutesInNetData({ExternalRoute(localOnLink, NetworkData::kRoutePreferenceMedium), + ExternalRoute(oldLocalOnLink, NetworkData::kRoutePreferenceMedium)}); + + // Keep checking the emitted RAs and make sure the prefix + // is included with smaller lifetime every time. + + sRaValidated = false; + + AdvanceTime(kMaxRaTxInterval * 1000); + + VerifyOrQuit(sRaValidated); + Log("Old on-link prefix is deprecating, remaining lifetime:%d", sOldOnLinkLifetime); + VerifyOrQuit(sOldOnLinkLifetime < oldPrefixLifetime); + oldPrefixLifetime = sOldOnLinkLifetime; + } + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // The local on-link prefix must be expired now and should no + // longer be seen in the emitted RA message. + + sRaValidated = false; + sExpectOldOnLinkPio = false; + + AdvanceTime(kMaxRaTxInterval * 1000); + + VerifyOrQuit(sRaValidated); + Log("Old on-link prefix is now expired"); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Validate the Network Data now only contains the current local + // on-link prefix. + + VerifyOmrPrefixInNetData(localOmr); + VerifyExternalRoutesInNetData({ExternalRoute(localOnLink, NetworkData::kRoutePreferenceMedium)}); + + //= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = + // Check behavior when ext PAN ID changes while the local on-link is being + // deprecated. + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Send an RA from router A with a new on-link (PIO) which is preferred over + // the local on-link prefix. + + SendRouterAdvert(routerAddressA, {Pio(onLinkPrefix, kValidLitime, kPreferredLifetime)}); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Validate that the local on-link prefix is deprecated. + + sRaValidated = false; + sExpectOldOnLinkPio = false; + sExpectedPio = kPioDeprecatingLocalOnLink; + + AdvanceTime(30000); + + VerifyOrQuit(sRaValidated); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Change the extended PAN ID. + + oldLocalOnLink = localOnLink; + oldPrefixLifetime = sOnLinkLifetime; + + dataset.mExtendedPanId = kExtPanId2; + SuccessOrQuit(otDatasetSetActive(sInstance, &dataset)); + + AdvanceTime(500); + SuccessOrQuit(sInstance->Get().GetOnLinkPrefix(localOnLink)); + Log("Local on-link prefix changed to %s from %s", localOnLink.ToString().AsCString(), + oldLocalOnLink.ToString().AsCString()); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Validate that the old local on-link prefix is still being included + // as PIO in the emitted RA. + + sRaValidated = false; + sExpectOldOnLinkPio = true; + sExpectedPio = kNoPio; + + AdvanceTime(30000); + + VerifyOrQuit(sRaValidated); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Validate that Network Data contains the old local on-link + // prefix along with entry from router A. + + VerifyOmrPrefixInNetData(localOmr); + VerifyExternalRoutesInNetData({ExternalRoute(oldLocalOnLink, NetworkData::kRoutePreferenceMedium), + ExternalRoute(onLinkPrefix, NetworkData::kRoutePreferenceMedium)}); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Wait for old local on-link prefix to expire. + + while (oldPrefixLifetime > kMaxRaTxInterval) + { + // Send same RA from router A to keep its on-link prefix alive. + + SendRouterAdvert(routerAddressA, {Pio(onLinkPrefix, kValidLitime, kPreferredLifetime)}); + + // Ensure Network Data entries remain as before. Mainly we still + // see the deprecating old local on-link prefix. + + VerifyExternalRoutesInNetData({ExternalRoute(oldLocalOnLink, NetworkData::kRoutePreferenceMedium), + ExternalRoute(onLinkPrefix, NetworkData::kRoutePreferenceMedium)}); + + // Keep checking the emitted RAs and make sure the prefix + // is included with smaller lifetime every time. + + sRaValidated = false; + + AdvanceTime(kMaxRaTxInterval * 1000); + + VerifyOrQuit(sRaValidated); + Log("Old on-link prefix is deprecating, remaining lifetime:%d", sOldOnLinkLifetime); + VerifyOrQuit(sOldOnLinkLifetime < oldPrefixLifetime); + oldPrefixLifetime = sOldOnLinkLifetime; + } + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // The old on-link prefix must be expired now and should no + // longer be seen in the emitted RA message. + + sRaValidated = false; + sExpectOldOnLinkPio = false; + + AdvanceTime(kMaxRaTxInterval * 1000); + + VerifyOrQuit(sRaValidated); + Log("Old on-link prefix is now expired"); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Validate the Network Data to now only contains entry from router A + + VerifyOmrPrefixInNetData(localOmr); + VerifyExternalRoutesInNetData({ExternalRoute(onLinkPrefix, NetworkData::kRoutePreferenceMedium)}); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + Log("End of TestExtPanIdChange"); + + testFreeInstance(sInstance); +} + +#if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE +void TestAutoEnableOfSrpServer(void) +{ + Ip6::Prefix localOnLink; + Ip6::Prefix localOmr; + Ip6::Address routerAddressA = AddressFromString("fd00::aaaa"); + Ip6::Prefix onLinkPrefix = PrefixFromString("2000:abba:baba::", 64); + + Log("--------------------------------------------------------------------------------------------"); + Log("TestAutoEnableOfSrpServer"); + + InitTest(); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Check SRP Server state and enable auto-enable mode + + otSrpServerSetAutoEnableMode(sInstance, true); + VerifyOrQuit(otSrpServerIsAutoEnableMode(sInstance)); + VerifyOrQuit(otSrpServerGetState(sInstance) == OT_SRP_SERVER_STATE_DISABLED); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Start Routing Manager. Check emitted RS and RA messages. + + sRsEmitted = false; + sRaValidated = false; + sExpectedPio = kPioAdvertisingLocalOnLink; + sExpectedRios.Clear(); + + SuccessOrQuit(sInstance->Get().SetEnabled(true)); + + SuccessOrQuit(sInstance->Get().GetOnLinkPrefix(localOnLink)); + SuccessOrQuit(sInstance->Get().GetOmrPrefix(localOmr)); + + Log("Local on-link prefix is %s", localOnLink.ToString().AsCString()); + Log("Local OMR prefix is %s", localOmr.ToString().AsCString()); + + sExpectedRios.Add(localOmr); + + AdvanceTime(30000); + + VerifyOrQuit(sRsEmitted); + VerifyOrQuit(sRaValidated); + VerifyOrQuit(sExpectedRios.SawAll()); + Log("Received RA was validated"); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Validate that SRP server was auto-enabled + + VerifyOrQuit(otSrpServerGetState(sInstance) != OT_SRP_SERVER_STATE_DISABLED); + Log("Srp::Server is enabled"); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Signal that infra if state changed and is no longer running. + // This should stop Routing Manager and in turn the SRP server. + + sRaValidated = false; + sExpectedPio = kPioDeprecatingLocalOnLink; + + Log("Signal infra if is not running"); + SuccessOrQuit(otPlatInfraIfStateChanged(sInstance, kInfraIfIndex, false)); + AdvanceTime(1); + + VerifyOrQuit(sRaValidated); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Check that SRP server is disabled. + + VerifyOrQuit(otSrpServerGetState(sInstance) == OT_SRP_SERVER_STATE_DISABLED); + Log("Srp::Server is disabled"); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Signal that infra if state changed and is running again. + + sRsEmitted = false; + sRaValidated = false; + sExpectedPio = kPioAdvertisingLocalOnLink; + sExpectedRios.Add(localOmr); + + Log("Signal infra if is running"); + SuccessOrQuit(otPlatInfraIfStateChanged(sInstance, kInfraIfIndex, true)); + + AdvanceTime(30000); + + VerifyOrQuit(sRsEmitted); + VerifyOrQuit(sRaValidated); + VerifyOrQuit(sExpectedRios.SawAll()); + Log("Received RA was validated"); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Check that SRP server is enabled again. + + VerifyOrQuit(otSrpServerGetState(sInstance) != OT_SRP_SERVER_STATE_DISABLED); + Log("Srp::Server is enabled"); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Disable `RoutingManager` explicitly. + + sRaValidated = false; + sExpectedPio = kPioDeprecatingLocalOnLink; + + Log("Disabling RoutingManager"); + SuccessOrQuit(sInstance->Get().SetEnabled(false)); + AdvanceTime(1); + + VerifyOrQuit(sRaValidated); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Check that SRP server is also disabled. + + VerifyOrQuit(otSrpServerGetState(sInstance) == OT_SRP_SERVER_STATE_DISABLED); + Log("Srp::Server is disabled"); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Disable auto-enable mode on SRP server. + + otSrpServerSetAutoEnableMode(sInstance, false); + VerifyOrQuit(!otSrpServerIsAutoEnableMode(sInstance)); + VerifyOrQuit(otSrpServerGetState(sInstance) == OT_SRP_SERVER_STATE_DISABLED); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Re-start Routing Manager. Check emitted RS and RA messages. + // This cycle, router A will send a RA including a PIO. + + sRsEmitted = false; + sRaValidated = false; + sExpectedPio = kNoPio; + sExpectedRios.Clear(); + + SuccessOrQuit(sInstance->Get().SetEnabled(true)); + + SuccessOrQuit(sInstance->Get().GetOnLinkPrefix(localOnLink)); + SuccessOrQuit(sInstance->Get().GetOmrPrefix(localOmr)); + + Log("Local on-link prefix is %s", localOnLink.ToString().AsCString()); + Log("Local OMR prefix is %s", localOmr.ToString().AsCString()); + + sExpectedRios.Add(localOmr); + + AdvanceTime(2000); + + SendRouterAdvert(routerAddressA, {Pio(onLinkPrefix, kValidLitime, kPreferredLifetime)}); + + AdvanceTime(30000); + + VerifyOrQuit(sRsEmitted); + VerifyOrQuit(sRaValidated); + VerifyOrQuit(sExpectedRios.SawAll()); + Log("Received RA was validated"); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Check that SRP server is still disabled. + + VerifyOrQuit(otSrpServerGetState(sInstance) == OT_SRP_SERVER_STATE_DISABLED); + Log("Srp::Server is disabled"); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Enable auto-enable mode on SRP server. Since `RoutingManager` + // is already done with initial policy evaluation, the SRP server + // must be started immediately. + + otSrpServerSetAutoEnableMode(sInstance, true); + VerifyOrQuit(otSrpServerIsAutoEnableMode(sInstance)); + + VerifyOrQuit(otSrpServerGetState(sInstance) != OT_SRP_SERVER_STATE_DISABLED); + Log("Srp::Server is enabled"); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Disable auto-enable mode on SRP server. It must not impact + // its current state and it should remain enabled. + + otSrpServerSetAutoEnableMode(sInstance, false); + VerifyOrQuit(!otSrpServerIsAutoEnableMode(sInstance)); + + AdvanceTime(2000); + VerifyOrQuit(otSrpServerGetState(sInstance) != OT_SRP_SERVER_STATE_DISABLED); + Log("Srp::Server is enabled"); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Signal that infra if state changed and is no longer running. + // This should stop Routing Manager. + + sRaValidated = false; + + Log("Signal infra if is not running"); + SuccessOrQuit(otPlatInfraIfStateChanged(sInstance, kInfraIfIndex, false)); + AdvanceTime(1); + + VerifyOrQuit(sRaValidated); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Re-enable auto-enable mode on SRP server. Since `RoutingManager` + // is stopped (infra if is down), the SRP serer must be stopped + // immediately. + + otSrpServerSetAutoEnableMode(sInstance, true); + VerifyOrQuit(otSrpServerIsAutoEnableMode(sInstance)); + + VerifyOrQuit(otSrpServerGetState(sInstance) == OT_SRP_SERVER_STATE_DISABLED); + Log("Srp::Server is disabled"); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + Log("End of TestAutoEnableOfSrpServer"); + testFreeInstance(sInstance); +} +#endif // OPENTHREAD_CONFIG_SRP_SERVER_ENABLE + #endif // OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE int main(void) @@ -1446,6 +1939,10 @@ int main(void) TestLocalOnLinkPrefixDeprecation(); #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE TestDomainPrefixAsOmr(); +#endif + TestExtPanIdChange(); +#if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE + TestAutoEnableOfSrpServer(); #endif printf("All tests passed\n"); #else diff --git a/tests/unit/test_srp_server.cpp b/tests/unit/test_srp_server.cpp new file mode 100644 index 00000000000..47974c7bc64 --- /dev/null +++ b/tests/unit/test_srp_server.cpp @@ -0,0 +1,712 @@ +/* + * Copyright (c) 2022, The OpenThread Authors. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include "test_platform.h" +#include "test_util.hpp" + +#include +#include +#include + +#include "common/arg_macros.hpp" +#include "common/array.hpp" +#include "common/instance.hpp" +#include "common/string.hpp" + +#if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE && OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE && \ + !OPENTHREAD_CONFIG_TIME_SYNC_ENABLE && !OPENTHREAD_PLATFORM_POSIX +#define ENABLE_SRP_TEST 1 +#else +#define ENABLE_SRP_TEST 0 +#endif + +#if ENABLE_SRP_TEST + +using namespace ot; + +// Logs a message and adds current time (sNow) as "::." +#define Log(...) \ + printf("%02u:%02u:%02u.%03u " OT_FIRST_ARG(__VA_ARGS__) "\n", (sNow / 36000000), (sNow / 60000) % 60, \ + (sNow / 1000) % 60, sNow % 1000 OT_REST_ARGS(__VA_ARGS__)) + +static constexpr uint16_t kMaxRaSize = 800; + +static ot::Instance *sInstance; + +static uint32_t sNow = 0; +static uint32_t sAlarmTime; +static bool sAlarmOn = false; + +static otRadioFrame sRadioTxFrame; +static uint8_t sRadioTxFramePsdu[OT_RADIO_FRAME_MAX_SIZE]; +static bool sRadioTxOngoing = false; + +//---------------------------------------------------------------------------------------------------------------------- +// Function prototypes + +void ProcessRadioTxAndTasklets(void); +void AdvanceTime(uint32_t aDuration); + +//---------------------------------------------------------------------------------------------------------------------- +// `otPlatRadio` + +extern "C" { + +otError otPlatRadioTransmit(otInstance *, otRadioFrame *) +{ + sRadioTxOngoing = true; + + return OT_ERROR_NONE; +} + +otRadioFrame *otPlatRadioGetTransmitBuffer(otInstance *) +{ + return &sRadioTxFrame; +} + +//---------------------------------------------------------------------------------------------------------------------- +// `otPlatAlaram` + +void otPlatAlarmMilliStop(otInstance *) +{ + sAlarmOn = false; +} + +void otPlatAlarmMilliStartAt(otInstance *, uint32_t aT0, uint32_t aDt) +{ + sAlarmOn = true; + sAlarmTime = aT0 + aDt; +} + +uint32_t otPlatAlarmMilliGetNow(void) +{ + return sNow; +} + +//---------------------------------------------------------------------------------------------------------------------- + +Array sHeapAllocatedPtrs; + +#if OPENTHREAD_CONFIG_HEAP_EXTERNAL_ENABLE +void *otPlatCAlloc(size_t aNum, size_t aSize) +{ + void *ptr = calloc(aNum, aSize); + + SuccessOrQuit(sHeapAllocatedPtrs.PushBack(ptr)); + + return ptr; +} + +void otPlatFree(void *aPtr) +{ + if (aPtr != nullptr) + { + void **entry = sHeapAllocatedPtrs.Find(aPtr); + + VerifyOrQuit(entry != nullptr, "A heap allocated item is freed twice"); + sHeapAllocatedPtrs.Remove(*entry); + } + + free(aPtr); +} +#endif + +#if OPENTHREAD_CONFIG_LOG_OUTPUT == OPENTHREAD_CONFIG_LOG_OUTPUT_PLATFORM_DEFINED +void otPlatLog(otLogLevel aLogLevel, otLogRegion aLogRegion, const char *aFormat, ...) +{ + OT_UNUSED_VARIABLE(aLogLevel); + OT_UNUSED_VARIABLE(aLogRegion); + + va_list args; + + printf(" "); + va_start(args, aFormat); + vprintf(aFormat, args); + va_end(args); + printf("\n"); +} +#endif + +} // extern "C" + +//--------------------------------------------------------------------------------------------------------------------- + +void ProcessRadioTxAndTasklets(void) +{ + do + { + if (sRadioTxOngoing) + { + sRadioTxOngoing = false; + otPlatRadioTxStarted(sInstance, &sRadioTxFrame); + otPlatRadioTxDone(sInstance, &sRadioTxFrame, nullptr, OT_ERROR_NONE); + } + + otTaskletsProcess(sInstance); + } while (otTaskletsArePending(sInstance)); +} + +void AdvanceTime(uint32_t aDuration) +{ + uint32_t time = sNow + aDuration; + + Log("AdvanceTime for %u.%03u", aDuration / 1000, aDuration % 1000); + + while (sAlarmTime <= time) + { + ProcessRadioTxAndTasklets(); + sNow = sAlarmTime; + otPlatAlarmMilliFired(sInstance); + } + + ProcessRadioTxAndTasklets(); + sNow = time; +} + +void InitTest(void) +{ + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Initialize OT instance. + + sNow = 0; + sInstance = static_cast(testInitInstance()); + + memset(&sRadioTxFrame, 0, sizeof(sRadioTxFrame)); + sRadioTxFrame.mPsdu = sRadioTxFramePsdu; + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Initialize Border Router and start Thread operation. + + SuccessOrQuit(otLinkSetPanId(sInstance, 0x1234)); + SuccessOrQuit(otIp6SetEnabled(sInstance, true)); + SuccessOrQuit(otThreadSetEnabled(sInstance, true)); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Ensure device starts as leader. + + AdvanceTime(10000); + + VerifyOrQuit(otThreadGetDeviceRole(sInstance) == OT_DEVICE_ROLE_LEADER); +} + +//--------------------------------------------------------------------------------------------------------------------- + +enum UpdateHandlerMode +{ + kAccept, // Accept all updates. + kReject, // Reject all updates. + kIgnore // Ignore all updates (do not call `otSrpServerHandleServiceUpdateResult()`). +}; + +static UpdateHandlerMode sUpdateHandlerMode = kAccept; +static bool sProcessedUpdateCallback = false; + +void HandleSrpServerUpdate(otSrpServerServiceUpdateId aId, + const otSrpServerHost * aHost, + uint32_t aTimeout, + void * aContext) +{ + Log("HandleSrpServerUpdate() called with %u, timeout:%u", aId, aTimeout); + + VerifyOrQuit(aHost != nullptr); + VerifyOrQuit(aContext == sInstance); + + sProcessedUpdateCallback = true; + + switch (sUpdateHandlerMode) + { + case kAccept: + otSrpServerHandleServiceUpdateResult(sInstance, aId, kErrorNone); + break; + case kReject: + otSrpServerHandleServiceUpdateResult(sInstance, aId, kErrorFailed); + break; + case kIgnore: + break; + } +} + +static bool sProcessedClientCallback = false; +static Error sLastClientCallbackError = kErrorNone; + +void HandleSrpClientCallback(otError aError, + const otSrpClientHostInfo *aHostInfo, + const otSrpClientService * aServices, + const otSrpClientService * aRemovedServices, + void * aContext) +{ + Log("HandleSrpClientCallback() called with error %s", ErrorToString(aError)); + + VerifyOrQuit(aContext == sInstance); + + sProcessedClientCallback = true; + sLastClientCallbackError = aError; + + OT_UNUSED_VARIABLE(aHostInfo); + OT_UNUSED_VARIABLE(aServices); + OT_UNUSED_VARIABLE(aRemovedServices); +} + +static const char kHostName[] = "myhost"; + +void PrepareService1(Srp::Client::Service &aService) +{ + static const char kServiceName[] = "_srv._udp"; + static const char kInstanceLabel[] = "srv-instance"; + static const char kSub1[] = "_sub1"; + static const char kSub2[] = "_V1234567"; + static const char kSub3[] = "_XYZWS"; + static const char * kSubLabels[] = {kSub1, kSub2, kSub3, nullptr}; + static const char kTxtKey1[] = "ABCD"; + static const uint8_t kTxtValue1[] = {'a', '0'}; + static const char kTxtKey2[] = "Z0"; + static const uint8_t kTxtValue2[] = {'1', '2', '3'}; + static const char kTxtKey3[] = "D"; + static const uint8_t kTxtValue3[] = {0}; + static const otDnsTxtEntry kTxtEntries[] = { + {kTxtKey1, kTxtValue1, sizeof(kTxtValue1)}, + {kTxtKey2, kTxtValue2, sizeof(kTxtValue2)}, + {kTxtKey3, kTxtValue3, sizeof(kTxtValue3)}, + }; + + aService.mName = kServiceName; + aService.mInstanceName = kInstanceLabel; + aService.mSubTypeLabels = kSubLabels; + aService.mTxtEntries = kTxtEntries; + aService.mNumTxtEntries = 3; + aService.mPort = 777; + aService.mWeight = 1; + aService.mPriority = 2; +} + +void PrepareService2(Srp::Client::Service &aService) +{ + static const char kService2Name[] = "_00112233667882554._matter._udp"; + static const char kInstance2Label[] = "ABCDEFGHI"; + static const char kSub4[] = "_44444444"; + static const char *kSubLabels2[] = {kSub4, nullptr}; + + aService.mName = kService2Name; + aService.mInstanceName = kInstance2Label; + aService.mSubTypeLabels = kSubLabels2; + aService.mTxtEntries = nullptr; + aService.mNumTxtEntries = 0; + aService.mPort = 555; + aService.mWeight = 0; + aService.mPriority = 3; +} + +void ValidateHost(Srp::Server &aServer, const char *aHostName) +{ + // Validate that only a host with `aHostName` is + // registered on SRP server. + + const Srp::Server::Host *host; + const char * name; + + Log("ValidateHost()"); + + host = aServer.GetNextHost(nullptr); + VerifyOrQuit(host != nullptr); + + name = host->GetFullName(); + Log("Hostname: %s", name); + + VerifyOrQuit(StringStartsWith(name, aHostName, kStringCaseInsensitiveMatch)); + VerifyOrQuit(name[strlen(aHostName)] == '.'); + + // Only one host on server + VerifyOrQuit(aServer.GetNextHost(host) == nullptr); +} + +//---------------------------------------------------------------------------------------------------------------------- + +void TestSrpServerBase(void) +{ + Srp::Server * srpServer; + Srp::Client * srpClient; + Srp::Client::Service service1; + Srp::Client::Service service2; + uint16_t heapAllocations; + + Log("--------------------------------------------------------------------------------------------"); + Log("TestSrpServerBase"); + + InitTest(); + + srpServer = &sInstance->Get(); + srpClient = &sInstance->Get(); + + heapAllocations = sHeapAllocatedPtrs.GetLength(); + + PrepareService1(service1); + PrepareService2(service2); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Start SRP server. + + SuccessOrQuit(srpServer->SetAddressMode(Srp::Server::kAddressModeUnicast)); + VerifyOrQuit(srpServer->GetAddressMode() == Srp::Server::kAddressModeUnicast); + + VerifyOrQuit(srpServer->GetState() == Srp::Server::kStateDisabled); + + srpServer->SetServiceHandler(HandleSrpServerUpdate, sInstance); + + srpServer->SetEnabled(true); + VerifyOrQuit(srpServer->GetState() != Srp::Server::kStateDisabled); + + AdvanceTime(10000); + VerifyOrQuit(srpServer->GetState() == Srp::Server::kStateRunning); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Start SRP client. + + srpClient->SetCallback(HandleSrpClientCallback, sInstance); + + srpClient->EnableAutoStartMode(nullptr, nullptr); + VerifyOrQuit(srpClient->IsAutoStartModeEnabled()); + + AdvanceTime(2000); + VerifyOrQuit(srpClient->IsRunning()); + + SuccessOrQuit(srpClient->SetHostName(kHostName)); + SuccessOrQuit(srpClient->EnableAutoHostAddress()); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Register a service, validate that update handler is called. + + SuccessOrQuit(srpClient->AddService(service1)); + + sUpdateHandlerMode = kAccept; + sProcessedUpdateCallback = false; + sProcessedClientCallback = false; + + AdvanceTime(2 * 1000); + + VerifyOrQuit(sProcessedUpdateCallback); + VerifyOrQuit(sProcessedClientCallback); + VerifyOrQuit(sLastClientCallbackError == kErrorNone); + + VerifyOrQuit(service1.GetState() == Srp::Client::kRegistered); + ValidateHost(*srpServer, kHostName); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Register a second service, validate that update handler is called. + + SuccessOrQuit(srpClient->AddService(service2)); + + sProcessedUpdateCallback = false; + sProcessedClientCallback = false; + + AdvanceTime(2 * 1000); + + VerifyOrQuit(sProcessedUpdateCallback); + VerifyOrQuit(sProcessedClientCallback); + VerifyOrQuit(sLastClientCallbackError == kErrorNone); + + VerifyOrQuit(service1.GetState() == Srp::Client::kRegistered); + VerifyOrQuit(service2.GetState() == Srp::Client::kRegistered); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Unregister first service, validate that update handler is called. + + SuccessOrQuit(srpClient->RemoveService(service1)); + + sProcessedUpdateCallback = false; + sProcessedClientCallback = false; + + AdvanceTime(2 * 1000); + + VerifyOrQuit(sProcessedUpdateCallback); + VerifyOrQuit(sProcessedClientCallback); + VerifyOrQuit(sLastClientCallbackError == kErrorNone); + + VerifyOrQuit(service1.GetState() == Srp::Client::kRemoved); + VerifyOrQuit(service2.GetState() == Srp::Client::kRegistered); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Disable SRP server, verify that all heap allocations by SRP server + // are freed. + + Log("Disabling SRP server"); + + srpServer->SetEnabled(false); + AdvanceTime(100); + + VerifyOrQuit(heapAllocations == sHeapAllocatedPtrs.GetLength()); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Finalize OT instance and validate all heap allocations are freed. + + Log("Finalizing OT instance"); + testFreeInstance(sInstance); + + VerifyOrQuit(sHeapAllocatedPtrs.IsEmpty()); + + Log("End of TestSrpServerBase"); +} + +void TestSrpServerReject(void) +{ + Srp::Server * srpServer; + Srp::Client * srpClient; + Srp::Client::Service service1; + Srp::Client::Service service2; + uint16_t heapAllocations; + + Log("--------------------------------------------------------------------------------------------"); + Log("TestSrpServerReject"); + + InitTest(); + + srpServer = &sInstance->Get(); + srpClient = &sInstance->Get(); + + heapAllocations = sHeapAllocatedPtrs.GetLength(); + + PrepareService1(service1); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Start SRP server. + + SuccessOrQuit(srpServer->SetAddressMode(Srp::Server::kAddressModeUnicast)); + VerifyOrQuit(srpServer->GetState() == Srp::Server::kStateDisabled); + + srpServer->SetServiceHandler(HandleSrpServerUpdate, sInstance); + + srpServer->SetEnabled(true); + VerifyOrQuit(srpServer->GetState() != Srp::Server::kStateDisabled); + + AdvanceTime(10000); + VerifyOrQuit(srpServer->GetState() == Srp::Server::kStateRunning); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Start SRP client. + + srpClient->SetCallback(HandleSrpClientCallback, sInstance); + + srpClient->EnableAutoStartMode(nullptr, nullptr); + VerifyOrQuit(srpClient->IsAutoStartModeEnabled()); + + AdvanceTime(2000); + VerifyOrQuit(srpClient->IsRunning()); + + SuccessOrQuit(srpClient->SetHostName(kHostName)); + SuccessOrQuit(srpClient->EnableAutoHostAddress()); + + sUpdateHandlerMode = kReject; + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Register a service, validate that update handler is called + // and rejected and no service is registered. + + SuccessOrQuit(srpClient->AddService(service1)); + + sProcessedUpdateCallback = false; + sProcessedClientCallback = false; + + AdvanceTime(2 * 1000); + + VerifyOrQuit(sProcessedUpdateCallback); + VerifyOrQuit(sProcessedClientCallback); + VerifyOrQuit(sLastClientCallbackError != kErrorNone); + + VerifyOrQuit(service1.GetState() != Srp::Client::kRegistered); + + VerifyOrQuit(srpServer->GetNextHost(nullptr) == nullptr); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Register a second service, validate that update handler is + // again called and update is rejected. + + SuccessOrQuit(srpClient->AddService(service2)); + + sProcessedUpdateCallback = false; + sProcessedClientCallback = false; + + AdvanceTime(2 * 1000); + + VerifyOrQuit(sProcessedUpdateCallback); + VerifyOrQuit(sProcessedClientCallback); + VerifyOrQuit(sLastClientCallbackError != kErrorNone); + + VerifyOrQuit(service1.GetState() != Srp::Client::kRegistered); + VerifyOrQuit(service2.GetState() != Srp::Client::kRegistered); + + VerifyOrQuit(srpServer->GetNextHost(nullptr) == nullptr); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Disable SRP server, verify that all heap allocations by SRP server + // are freed. + + Log("Disabling SRP server"); + + srpServer->SetEnabled(false); + AdvanceTime(100); + + VerifyOrQuit(heapAllocations == sHeapAllocatedPtrs.GetLength()); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Finalize OT instance and validate all heap allocations are freed. + + Log("Finalizing OT instance"); + testFreeInstance(sInstance); + + VerifyOrQuit(sHeapAllocatedPtrs.IsEmpty()); + + Log("End of TestSrpServerReject"); +} + +void TestSrpServerIgnore(void) +{ + Srp::Server * srpServer; + Srp::Client * srpClient; + Srp::Client::Service service1; + Srp::Client::Service service2; + uint16_t heapAllocations; + + Log("--------------------------------------------------------------------------------------------"); + Log("TestSrpServerIgnore"); + + InitTest(); + + srpServer = &sInstance->Get(); + srpClient = &sInstance->Get(); + + heapAllocations = sHeapAllocatedPtrs.GetLength(); + + PrepareService1(service1); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Start SRP server. + + SuccessOrQuit(srpServer->SetAddressMode(Srp::Server::kAddressModeUnicast)); + VerifyOrQuit(srpServer->GetState() == Srp::Server::kStateDisabled); + + srpServer->SetServiceHandler(HandleSrpServerUpdate, sInstance); + + srpServer->SetEnabled(true); + VerifyOrQuit(srpServer->GetState() != Srp::Server::kStateDisabled); + + AdvanceTime(10000); + VerifyOrQuit(srpServer->GetState() == Srp::Server::kStateRunning); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Start SRP client. + + srpClient->SetCallback(HandleSrpClientCallback, sInstance); + + srpClient->EnableAutoStartMode(nullptr, nullptr); + VerifyOrQuit(srpClient->IsAutoStartModeEnabled()); + + AdvanceTime(2000); + VerifyOrQuit(srpClient->IsRunning()); + + SuccessOrQuit(srpClient->SetHostName(kHostName)); + SuccessOrQuit(srpClient->EnableAutoHostAddress()); + + sUpdateHandlerMode = kIgnore; + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Register a service, validate that update handler is called + // and ignored the update and no service is registered. + + SuccessOrQuit(srpClient->AddService(service1)); + + sProcessedUpdateCallback = false; + sProcessedClientCallback = false; + + AdvanceTime(2 * 1000); + + VerifyOrQuit(sProcessedUpdateCallback); + VerifyOrQuit(sProcessedClientCallback); + VerifyOrQuit(sLastClientCallbackError != kErrorNone); + + VerifyOrQuit(service1.GetState() != Srp::Client::kRegistered); + + VerifyOrQuit(srpServer->GetNextHost(nullptr) == nullptr); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Register a second service, validate that update handler is + // again called and update is still ignored. + + SuccessOrQuit(srpClient->AddService(service2)); + + sProcessedUpdateCallback = false; + sProcessedClientCallback = false; + + AdvanceTime(2 * 1000); + + VerifyOrQuit(sProcessedUpdateCallback); + VerifyOrQuit(sProcessedClientCallback); + VerifyOrQuit(sLastClientCallbackError != kErrorNone); + + VerifyOrQuit(service1.GetState() != Srp::Client::kRegistered); + VerifyOrQuit(service2.GetState() != Srp::Client::kRegistered); + + VerifyOrQuit(srpServer->GetNextHost(nullptr) == nullptr); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Disable SRP server, verify that all heap allocations by SRP server + // are freed. + + Log("Disabling SRP server"); + + srpServer->SetEnabled(false); + AdvanceTime(100); + + VerifyOrQuit(heapAllocations == sHeapAllocatedPtrs.GetLength()); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Finalize OT instance and validate all heap allocations are freed. + + Log("Finalizing OT instance"); + testFreeInstance(sInstance); + + VerifyOrQuit(sHeapAllocatedPtrs.IsEmpty()); + + Log("End of TestSrpServerIgnore"); +} + +#endif // ENABLE_SRP_TEST + +int main(void) +{ +#if ENABLE_SRP_TEST + TestSrpServerBase(); + TestSrpServerReject(); + TestSrpServerIgnore(); + printf("All tests passed\n"); +#else + printf("SRP_SERVER or SRP_CLIENT feature is not enabled\n"); +#endif + + return 0; +} diff --git a/third_party/tcplp/CMakeLists.txt b/third_party/tcplp/CMakeLists.txt index 3ea41b6ca52..9ec23b8d78b 100644 --- a/third_party/tcplp/CMakeLists.txt +++ b/third_party/tcplp/CMakeLists.txt @@ -43,7 +43,6 @@ set(src_tcplp lib/lbuf.c ) -set(tcplp_static_target "tcplp") string(REPLACE "-Wsign-compare" "" CMAKE_C_FLAGS "${CMAKE_C_FLAGS}") @@ -51,40 +50,58 @@ string(REPLACE "-Wconversion" "" CMAKE_C_FLAGS "${CMAKE_C_FLAGS}") string(REPLACE "-Wunused-parameter" "" CMAKE_C_FLAGS "${CMAKE_C_FLAGS}") -add_library(${tcplp_static_target} STATIC ${src_tcplp}) -target_compile_options(${tcplp_static_target} - PRIVATE - "-Wno-sign-compare" - "-Wno-unused-parameter" -) -set_target_properties(${tcplp_static_target} PROPERTIES OUTPUT_NAME tcplp) -target_include_directories(${tcplp_static_target} - PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}/bsdtcp - ${CMAKE_CURRENT_SOURCE_DIR}/lib - PRIVATE - ${OT_PUBLIC_INCLUDES} -) - -target_link_libraries(${tcplp_static_target} - PRIVATE - ot-config -) +if(OT_FTD) + add_library(tcplp-ftd STATIC ${src_tcplp}) + target_compile_options(tcplp-ftd + PRIVATE + "-Wno-sign-compare" + "-Wno-unused-parameter" + ) + set_target_properties(tcplp-ftd PROPERTIES OUTPUT_NAME tcplp-ftd) + target_include_directories(tcplp-ftd + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/bsdtcp + ${CMAKE_CURRENT_SOURCE_DIR}/lib + PRIVATE + ${OT_PUBLIC_INCLUDES} + ) -# TCPlp calls functions that are defined by the core OpenThread (like -# "otMessageWrite()"), so we need to add the core library (FTD or MTD, as -# appropriate) as a link dependency. + target_link_libraries(tcplp-ftd + PRIVATE + ot-config + ) -if(OT_FTD) - target_link_libraries(${tcplp_static_target} + target_link_libraries(tcplp-ftd PRIVATE openthread-ftd ) + endif() if(OT_MTD) - target_link_libraries(${tcplp_static_target} + add_library(tcplp-mtd STATIC ${src_tcplp}) + target_compile_options(tcplp-mtd + PRIVATE + "-Wno-sign-compare" + "-Wno-unused-parameter" + ) + set_target_properties(tcplp-mtd PROPERTIES OUTPUT_NAME tcplp-mtd) + target_include_directories(tcplp-mtd + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/bsdtcp + ${CMAKE_CURRENT_SOURCE_DIR}/lib + PRIVATE + ${OT_PUBLIC_INCLUDES} + ) + + target_link_libraries(tcplp-mtd + PRIVATE + ot-config + ) + + target_link_libraries(tcplp-mtd PRIVATE openthread-mtd ) + endif() diff --git a/third_party/tcplp/lib/lbuf.c b/third_party/tcplp/lib/lbuf.c index 91dd4561bbe..ba39c5d1c7e 100644 --- a/third_party/tcplp/lib/lbuf.c +++ b/third_party/tcplp/lib/lbuf.c @@ -55,6 +55,7 @@ void lbuf_append(struct lbufhead* buffer, otLinkedBuffer* newentry) { void lbuf_extend(struct lbufhead* buffer, size_t numbytes) { buffer->tail->mLength += numbytes; + buffer->length += numbytes; } size_t lbuf_pop(struct lbufhead* buffer, size_t numbytes, uint32_t* ntraversed) { diff --git a/third_party/tcplp/lib/test/Makefile b/third_party/tcplp/lib/test/Makefile new file mode 100644 index 00000000000..5613cbd18d4 --- /dev/null +++ b/third_party/tcplp/lib/test/Makefile @@ -0,0 +1,16 @@ +CC=clang +CFLAGS=-I ../../../../include -O2 -Wall + +all: test_all + +%.o: ../%.c + clang -c $(CFLAGS) $< -o $@ + +test_all.o: test_all.c + clang -c $(CFLAGS) test_all.c -o $@ + +test_all: test_all.o cbuf.o lbuf.o bitmap.o + clang test_all.o cbuf.o lbuf.o bitmap.o -o test_all + +clean: + rm -f *.o test_all diff --git a/third_party/tcplp/lib/test/test_all.c b/third_party/tcplp/lib/test/test_all.c new file mode 100644 index 00000000000..9884259f40d --- /dev/null +++ b/third_party/tcplp/lib/test/test_all.c @@ -0,0 +1,219 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "../bitmap.h" +#include "../cbuf.h" + +uint32_t num_tests_passed = 0; +uint32_t num_tests_failed = 0; + +uint16_t otMessageRead(const otMessage *aMessage, uint16_t aOffset, void *aBuf, uint16_t aLength) { + return aLength; +} + +int otMessageWrite(otMessage *aMessage, uint16_t aOffset, const void *aBuf, uint16_t aLength) { + return aLength; +} + +void bmp_print(uint8_t* buf, size_t buflen) { + size_t i; + for (i = 0; i < buflen; i++) { + printf("%02X", buf[i]); + } + printf("\n"); +} + +void bmp_test(const char* test_name, uint8_t* buf, size_t buflen, const char* contents) { + char buf_string[(buflen << 1) + 1]; + buf_string[0] = '\0'; + for (size_t i = 0; i < buflen; i++) { + snprintf(&buf_string[i << 1], 3, "%02X", buf[i]); + } + if (strcmp(contents, buf_string) == 0) { + printf("%s: PASS\n", test_name); + num_tests_passed++; + } else { + printf("%s: FAIL: %s vs. %s\n", test_name, contents, buf_string); + num_tests_failed++; + } +} + +void test_bmp() { + size_t test_bmp_size = 8; + uint8_t buffer[test_bmp_size]; + + bmp_init(buffer, test_bmp_size); + bmp_test("bmp_init", buffer, test_bmp_size, "0000000000000000"); + + bmp_setrange(buffer, 11, 7); + bmp_test("bmp_setrange 1", buffer, test_bmp_size, "001FC00000000000"); + + bmp_setrange(buffer, 35, 3); + bmp_test("bmp_setrange 2", buffer, test_bmp_size, "001FC0001C000000"); + + bmp_setrange(buffer, 47, 4); + bmp_test("bmp_setrange 3", buffer, test_bmp_size, "001FC0001C01E000"); + + bmp_swap(buffer, 3, 36, 1); + bmp_test("bmp_swap 1", buffer, test_bmp_size, "101FC0001401E000"); + + bmp_swap(buffer, 0, 40, 24); + bmp_test("bmp_swap 2", buffer, test_bmp_size, "01E0000014101FC0"); + + bmp_swap(buffer, 2, 42, 15); + bmp_test("bmp_swap 3", buffer, test_bmp_size, "101F80001401E040"); + + bmp_swap(buffer, 13, 23, 2); + bmp_test("bmp_swap 4", buffer, test_bmp_size, "101981801401E040"); + + bmp_swap(buffer, 0, 35, 24); + bmp_test("bmp_swap 5", buffer, test_bmp_size, "A00F028002033020"); +} + +void cbuf_test(const char* test_name, struct cbufhead* chdr, const char* contents) { + char buf_string[chdr->size + 1]; + struct otLinkedBuffer first; + struct otLinkedBuffer second; + cbuf_reference(chdr, &first, &second); + + + memcpy(&buf_string[0], &first.mData[0], first.mLength); + if (first.mNext != NULL) { + assert(first.mNext == &second); + memcpy(&buf_string[first.mLength], &second.mData[0], second.mLength); + assert(second.mNext == NULL); + buf_string[first.mLength + second.mLength] = '\0'; + } else { + buf_string[first.mLength] = '\0'; + } + + if (strcmp(contents, buf_string) == 0) { + printf("%s: PASS\n", test_name); + num_tests_passed++; + } else { + printf("%s: FAIL: %s (%zu) vs. %s (%zu)\n", test_name, contents, strlen(contents), buf_string, strlen(buf_string)); + num_tests_failed++; + } +} + +void cbuf_write_string(struct cbufhead* chdr, const char* string) { + cbuf_write(chdr, string, 0, strlen(string), cbuf_copy_into_buffer); +} + +void test_cbuf() { + uint8_t buffer[65]; + uint8_t bitmap[8]; + struct cbufhead chdr; + + cbuf_init(&chdr, buffer, 64); // capacity is actually 64 + cbuf_test("cbuf_init", &chdr, ""); + + cbuf_write_string(&chdr, "abcdefghijklmnopqrstuvwxyz0123456789"); + cbuf_test("cbuf_write", &chdr, "abcdefghijklmnopqrstuvwxyz0123456789"); + + cbuf_pop(&chdr, 1); + cbuf_test("cbuf_pop", &chdr, "bcdefghijklmnopqrstuvwxyz0123456789"); + + cbuf_pop(&chdr, 5); + cbuf_test("cbuf_pop", &chdr, "ghijklmnopqrstuvwxyz0123456789"); + + cbuf_write_string(&chdr, "abcdefghijklmnopqrstuvwxyz01234567"); + cbuf_test("cbuf_write", &chdr, "ghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz01234567"); + + cbuf_contiguify(&chdr, NULL); + cbuf_test("cbuf_contiguify", &chdr, "ghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz01234567"); + + cbuf_pop(&chdr, 50); + cbuf_test("cbuf_pop", &chdr, "uvwxyz01234567"); + + cbuf_write_string(&chdr, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"); // yz overflows and isn't written + cbuf_test("cbuf_write", &chdr, "uvwxyz01234567ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwx"); + + cbuf_contiguify(&chdr, NULL); + cbuf_test("cbuf_contiguify", &chdr, "uvwxyz01234567ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwx"); + + cbuf_contiguify(&chdr, NULL); // check that a second "contiguify" operation doesn't mess things up + cbuf_test("cbuf_contiguify", &chdr, "uvwxyz01234567ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwx"); + + cbuf_pop(&chdr, 20); + cbuf_test("cbuf_pop", &chdr, "GHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwx"); + + cbuf_write_string(&chdr, "yz"); + cbuf_test("cbuf_write", &chdr, "GHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"); + + bmp_init(bitmap, 8); + bmp_test("bmp_init", bitmap, 8, "0000000000000000"); + + cbuf_reass_write(&chdr, 4, "@@@@@@@@@@@", 0, 11, bitmap, NULL, cbuf_copy_from_buffer); + cbuf_test("cbuf_reass_write (cbuf)", &chdr, "GHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"); + bmp_test("cbuf_reass_write (bitmap)", bitmap, 8, "03FF800000000000"); + + cbuf_contiguify(&chdr, bitmap); + cbuf_test("cbuf_contiguify (cbuf)", &chdr, "GHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"); + bmp_test("cbuf_reass_write (bitmap)", bitmap, 8, "0000000000003FF8"); + + cbuf_write_string(&chdr, "1234"); + cbuf_test("cbuf_write", &chdr, "GHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234"); + + cbuf_reass_merge(&chdr, 9, bitmap); + cbuf_test("cbuf_reass_merge (cbuf)", &chdr, "GHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234@@@@@@@@@"); + bmp_test("cbuf_reass_merge (bitmap)", bitmap, 8, "0000000000000018"); + + cbuf_reass_merge(&chdr, 2, bitmap); + cbuf_test("cbuf_reass_merge (cbuf)", &chdr, "GHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234@@@@@@@@@@@"); + bmp_test("cbuf_reass_merge (bitmap)", bitmap, 8, "0000000000000000"); + + cbuf_pop(&chdr, 61); + cbuf_test("cbuf_pop", &chdr, ""); +} + +void test_cbuf_2() { + uint8_t buffer[32]; + uint8_t bitmap[4]; + struct cbufhead chdr; + + cbuf_init(&chdr, buffer, 32); + cbuf_test("cbuf_init", &chdr, ""); + + bmp_init(bitmap, 4); + bmp_test("bmp_init", bitmap, 4, "00000000"); + + cbuf_reass_write(&chdr, 6, "abcdefghijklmnopqrstuvwxyz", 0, 26, bitmap, NULL, cbuf_copy_from_buffer); + cbuf_test("cbuf_reass_write (cbuf)", &chdr, ""); + bmp_test("cbuf_reass_write (bitmap)", bitmap, 4, "03FFFFFF"); + + cbuf_write_string(&chdr, "ASDFGH"); + cbuf_test("cbuf_write (cbuf)", &chdr, "ASDFGH"); + bmp_test("cbuf_write (bitmap)", bitmap, 4, "03FFFFFF"); + + cbuf_pop(&chdr, 6); + cbuf_test("cbuf_pop (cbuf)", &chdr, ""); + bmp_test("cbuf_pop (bitmap)", bitmap, 4, "03FFFFFF"); + + cbuf_reass_write(&chdr, 26, "!@#$^&", 0, 6, bitmap, NULL, cbuf_copy_from_buffer); + cbuf_test("cbuf_reass_write (cbuf)", &chdr, ""); + bmp_test("cbuf_reass_write (bitmap)", bitmap, 4, "FFFFFFFF"); + + printf("Count Set: %d (should be at least 32)\n", (int) cbuf_reass_count_set(&chdr, 0, bitmap, 32)); + + cbuf_reass_merge(&chdr, 32, bitmap); + cbuf_test("cbuf_reass_merge (cbuf)", &chdr, "abcdefghijklmnopqrstuvwxyz!@#$^&"); + bmp_test("cbuf_reass_merge (bitmap)", bitmap, 4, "00000000"); +} + +int main(int argc, char** argv) { + test_bmp(); + test_cbuf(); + test_cbuf_2(); + + printf("%" PRIu32 " tests passed (out of %" PRIu32 ")\n", num_tests_passed, num_tests_passed + num_tests_failed); + if (num_tests_failed != 0) { + return EXIT_FAILURE; + } + return EXIT_SUCCESS; +} diff --git a/tools/harness-simulation/README.md b/tools/harness-simulation/README.md index 61e1a95c3c1..602fca53e60 100644 --- a/tools/harness-simulation/README.md +++ b/tools/harness-simulation/README.md @@ -10,33 +10,19 @@ Platform developers should modify the THCI implementation and/or the SI implemen ## POSIX Environment Setup -1. Build OpenThread to generate standalone OpenThread simulation `ot-cli-ftd`. For example, to run OpenThread 1.2 test cases, run the following command in the top directory of OpenThread: +1. Open the JSON format configuration file `tools/harness-simulation/posix/config.yml`: - ```bash - $ CFLAGS='-DOPENTHREAD_CONFIG_IP6_MAX_EXT_MCAST_ADDRS=8' \ - CXXFLAGS='-DOPENTHREAD_CONFIG_IP6_MAX_EXT_MCAST_ADDRS=8' \ - script/cmake-build simulation \ - -DOT_THREAD_VERSION=1.2 \ - -DOT_DUA=ON \ - -DOT_MLR=ON \ - -DOT_COMMISSIONER=ON \ - -DOT_CSL_RECEIVER=ON \ - -DOT_SIMULATION_MAX_NETWORK_SIZE=64 - ``` - - Then `ot-cli-ftd` is built in the directory `build/simulation/examples/apps/cli/`. + - Edit the value of `ot_path` to the absolute path where the top directory of the OpenThread repository is located. For example, change the value of `ot_path` to `/home//repo/openthread`. + - For each entry in `ot_build.ot`, update the value of `number` to be the number of OT FTD simulations needed with the corresponding version. + - For each entry in `ot_build.otbr`, update the value of `number` to be the number of OTBR simulations needed with the corresponding version. + - The numbers above can be adjusted according to the requirement of test cases. + - Edit the value of `ssh.username` to the username to be used for connecting to the remote POSIX environment. + - Edit the value of `ssh.password` to the password corresponding to the username above. + - Edit the value of `discovery_ifname` to the network interface that the Harness will connect to. -2. Open the configuration file `config.py`: + Note that it may be time-consuming to build all versions of `ot-cli-ftd`s and OTBR Docker images especially on devices such as Raspberry Pis. - - Edit the value of `OT_PATH` to the absolute path where the top directory of the OpenThread repository is located. For example, change the value of `OT_PATH` to `/home//repo/openthread`. - -3. Run the `build_docker_image.sh` with the environment variable `OT_PATH` set properly. `OT_PATH` should be the same as that in the previous step. For example run the following command: - - ```bash - $ OT_PATH=~/repo/openthread ./build_docker_image.sh - ``` - -4. Run the installation script. +2. Run the installation script. ```bash $ tools/harness-simulation/posix/install.sh @@ -44,33 +30,21 @@ Platform developers should modify the THCI implementation and/or the SI implemen ## Test Harness Environment Setup -1. Double click the file `harness\install.bat` on the machine which installed Harness. - -2. Check the configuration file `C:\GRL\Thread1.2\Thread_Harness\simulation\config.py` +1. Copy the directory `tools/harness-simulation` from the POSIX machine to the Windows machine, and then switch to that directory. - - Edit the value of `REMOTE_USERNAME` to the username expected to connect to on the remote POSIX environment. - - Edit the value of `REMOTE_PASSWORD` to the password corresponding to the username above. - - Edit the value of `REMOTE_OT_PATH` to the absolute path where the top directory of the OpenThread repository is located. - -3. Add the additional simulation device information in `harness\Web\data\deviceInputFields.xml` to `C:\GRL\Thread1.2\Web\data\deviceInputFields.xml`. +2. Double click the file `harness\install.bat` on Windows. ## Run Test Harness on Simulation -1. On POSIX machine, change directory to the top of OpenThread repository, and run the following commands. +1. On the POSIX machine, change directory to the top of the OpenThread repository, and run the following commands. ```bash $ cd tools/harness-simulation/posix - $ python3 launch_testbed.py \ - --interface=eth0 \ - --ot=6 \ - --otbr=4 \ - --sniffer=2 + $ ./launch_testbed.py -c config.yml ``` - This example starts 6 OT FTD simulations, 4 OTBR simulations, and 2 sniffer simulations and can be discovered on `eth0`. - - The arguments can be adjusted according to the requirement of test cases. + This example starts several OT FTD simulations, OTBR simulations, and sniffer simulations and can be discovered on `eth0`. The number of each type of simulation is specified in the configuration file `config.yml`. -2. Run Test Harness. The information field of the device is encoded as `@` for FTDs and `otbr_@` for BRs. Choose the proper device as the DUT accordingly. +2. Run the Test Harness. The information field of the device is encoded as `_@`. Choose the desired device as the DUT. 3. Select one or more test cases to start the test. diff --git a/tools/harness-simulation/harness/Thread_Harness/Sniffer/SimSniffer.py b/tools/harness-simulation/harness/Thread_Harness/Sniffer/SimSniffer.py index 194b6ad0313..90bc43f3daf 100644 --- a/tools/harness-simulation/harness/Thread_Harness/Sniffer/SimSniffer.py +++ b/tools/harness-simulation/harness/Thread_Harness/Sniffer/SimSniffer.py @@ -29,6 +29,7 @@ import grpc import ipaddress +import itertools import json import netifaces import select @@ -38,6 +39,9 @@ import win32api import winreg as wr +from GRLLibs.UtilityModules.ModuleHelper import ModuleHelper +from GRLLibs.UtilityModules.SnifferManager import SnifferManager +from GRLLibs.UtilityModules.TopologyManager import TopologyManager from Sniffer.ISniffer import ISniffer from THCI.OpenThread import watched from simulation.Sniffer.proto import sniffer_pb2 @@ -56,7 +60,79 @@ WINREG_KEY = r'SYSTEM\CurrentControlSet\Control\Network\{4d36e972-e325-11ce-bfc1-08002be10318}' +# When Harness requires an RF shield box, it will pop up a message box via `ModuleHelper.UIMsgBox` +# Replace the function to add and remove an RF enclosure simulation automatically without popping up a window +def UIMsgBoxDecorator(UIMsgBox, replaceFuncs): + + @staticmethod + def UIMsgBoxWrapper(msg='Confirm ??', + title='User Input Required', + inputRequired=False, + default='', + choices=None, + timeout=None, + isPrompt=False): + func = replaceFuncs.get((msg, title)) + if func is None: + return UIMsgBox(msg, title, inputRequired, default, choices, timeout, isPrompt) + else: + return func() + + return UIMsgBoxWrapper + + +class DeviceManager: + + def __init__(self): + deviceManager = TopologyManager.m_DeviceManager + self._devices = {'DUT': deviceManager.AutoDUTDeviceObject.THCI_Object} + for device_obj in deviceManager.DeviceList.values(): + if device_obj.IsDeviceUsed: + self._devices[device_obj.DeviceTopologyInfo] = device_obj.THCI_Object + + def __getitem__(self, deviceName): + device = self._devices.get(deviceName) + if device is None: + raise KeyError('Device Name "%s" not found' % deviceName) + return device + + +class RFEnclosureManager: + + def __init__(self, deviceNames1, deviceNames2, snifferPartitionId): + self.deviceNames1 = deviceNames1 + self.deviceNames2 = deviceNames2 + self.snifferPartitionId = snifferPartitionId + + def placeRFEnclosure(self): + devices = DeviceManager() + self._partition1 = [devices[role] for role in self.deviceNames1] + self._partition2 = [devices[role] for role in self.deviceNames2] + sniffer_denied_partition = self._partition1 if self.snifferPartitionId == 2 else self._partition2 + + for device1 in self._partition1: + for device2 in self._partition2: + device1.addBlockedNodeId(device2.node_id) + device2.addBlockedNodeId(device1.node_id) + + for sniffer in SnifferManager.SnifferObjects.values(): + if sniffer.isSnifferCapturing(): + sniffer.filterNodes(device.node_id for device in sniffer_denied_partition) + + def removeRFEnclosure(self): + for device in itertools.chain(self._partition1, self._partition2): + device.clearBlockedNodeIds() + + for sniffer in SnifferManager.SnifferObjects.values(): + if sniffer.isSnifferCapturing(): + sniffer.filterNodes(()) + + self._partition1 = None + self._partition2 = None + + class SimSniffer(ISniffer): + replaced_msgbox = False @watched def __init__(self, **kwargs): @@ -66,12 +142,47 @@ def __init__(self, **kwargs): self._local_pcapng_location = None if self.addr_port is not None: + # Replace `ModuleHelper.UIMsgBox` only when simulation devices exist + self._replaceMsgBox() + self._sniffer = grpc.insecure_channel(self.addr_port) self._stub = sniffer_pb2_grpc.SnifferStub(self._sniffer) # Close the sniffer only when Harness exits win32api.SetConsoleCtrlHandler(self.__disconnect, True) + @watched + def _replaceMsgBox(self): + # Replace the function only once + if SimSniffer.replaced_msgbox: + return + SimSniffer.replaced_msgbox = True + + test_9_2_9_leader = RFEnclosureManager(['Router_1', 'Router_2'], ['DUT', 'Commissioner'], 1) + test_9_2_9_router = RFEnclosureManager(['DUT', 'Router_2'], ['Leader', 'Commissioner'], 1) + test_9_2_10_router = RFEnclosureManager(['Leader', 'Commissioner'], ['DUT', 'MED_1', 'SED_1'], 2) + + # Alter the behavior of `ModuleHelper.UIMsgBox` only when it comes to the following test cases: + # - Leader 9.2.9 + # - Router 9.2.9 + # - Router 9.2.10 + ModuleHelper.UIMsgBox = UIMsgBoxDecorator( + ModuleHelper.UIMsgBox, + replaceFuncs={ + ("Place [Router1, Router2 and Sniffer]
or
[DUT and Commissioner]
in an RF enclosure ", "Shield Devices"): + test_9_2_9_leader.placeRFEnclosure, + ("Remove [Router1, Router2 and Sniffer]
or
[DUT and Commissioner]
from RF enclosure ", "Unshield Devices"): + test_9_2_9_leader.removeRFEnclosure, + ("Place [DUT,Router2 and sniffer]
or
[Leader and Commissioner]
in an RF enclosure ", "Shield Devices"): + test_9_2_9_router.placeRFEnclosure, + ("Remove [DUT, Router2 and sniffer]
or
[Leader and Commissioner]
from RF enclosure ", "Unshield Devices"): + test_9_2_9_router.removeRFEnclosure, + ("Place the
[Leader and Commissioner] devices
in an RF enclosure ", "Shield Devices"): + test_9_2_10_router.placeRFEnclosure, + ("Remove
[Leader and Commissioner]
from RF enclosure ", "Unshield Devices"): + test_9_2_10_router.removeRFEnclosure, + }) + def __repr__(self): return '%r' % self.__dict__ @@ -164,6 +275,18 @@ def stopSniffer(self): self.is_active = False + @watched + def filterNodes(self, nodeids): + if not self.is_active: + return + + request = sniffer_pb2.FilterNodesRequest() + request.nodeids.extend(nodeids) + + response = self._stub.FilterNodes(request) + if response.status != sniffer_pb2.OK: + raise RuntimeError('filterNodes error: %s' % sniffer_pb2.Status.Name(response.status)) + def __disconnect(self, dwCtrlType): if self._sniffer is not None: self._sniffer.close() diff --git a/tools/harness-simulation/harness/Thread_Harness/THCI/OpenThread_BR_Sim.py b/tools/harness-simulation/harness/Thread_Harness/THCI/OpenThread_BR_Sim.py index e2b04565e02..17c8698b7a3 100644 --- a/tools/harness-simulation/harness/Thread_Harness/THCI/OpenThread_BR_Sim.py +++ b/tools/harness-simulation/harness/Thread_Harness/THCI/OpenThread_BR_Sim.py @@ -39,12 +39,15 @@ import sys import time +from THCI.IThci import IThci from THCI.OpenThread import watched from THCI.OpenThread_BR import OpenThread_BR -from simulation.config import (REMOTE_USERNAME, REMOTE_PASSWORD, REMOTE_PORT) +from simulation.config import load_config logging.getLogger('paramiko').setLevel(logging.WARNING) +config = load_config() + class SSHHandle(object): # Unit: second @@ -109,11 +112,8 @@ def bash(self, cmd, timeout): class OpenThread_BR_Sim(OpenThread_BR): def _getHandle(self): - assert self.connectType == 'ip' - assert '@' in self.telnetIp self.log('SSH connecting ...') - docker_name, ssh_ip = self.telnetIp.split('@') - return SSHHandle(ssh_ip, self.telnetPort, self.telnetUsername, self.telnetPassword, docker_name) + return SSHHandle(self.ssh_ip, self.telnetPort, self.telnetUsername, self.telnetPassword, self.docker_name) @watched def _parseConnectionParams(self, params): @@ -121,15 +121,20 @@ def _parseConnectionParams(self, params): if '@' not in discovery_add: raise ValueError('%r in the field `add` is invalid' % discovery_add) - docker_name, ssh_ip = discovery_add.split('@') + self.docker_name, self.ssh_ip = discovery_add.split('@') + self.tag, self.node_id = self.docker_name.split('_') + self.node_id = int(self.node_id) # Let it crash if it is an invalid IP address - ipaddress.ip_address(ssh_ip) + ipaddress.ip_address(self.ssh_ip) self.connectType = 'ip' self.telnetIp = self.port = discovery_add - self.telnetPort = REMOTE_PORT - self.telnetUsername = REMOTE_USERNAME - self.telnetPassword = REMOTE_PASSWORD + + global config + ssh = config['ssh'] + self.telnetPort = ssh['port'] + self.telnetUsername = ssh['username'] + self.telnetPassword = ssh['password'] self.extraParams = { 'cmd-start-otbr-agent': 'service otbr-agent start', @@ -137,3 +142,6 @@ def _parseConnectionParams(self, params): 'cmd-restart-otbr-agent': 'service otbr-agent restart', 'cmd-restart-radvd': 'service radvd stop; service radvd start', } + + +assert issubclass(OpenThread_BR_Sim, IThci) diff --git a/tools/harness-simulation/harness/Thread_Harness/THCI/OpenThread_Sim.py b/tools/harness-simulation/harness/Thread_Harness/THCI/OpenThread_Sim.py index 00f876487ba..18c622ec067 100644 --- a/tools/harness-simulation/harness/Thread_Harness/THCI/OpenThread_Sim.py +++ b/tools/harness-simulation/harness/Thread_Harness/THCI/OpenThread_Sim.py @@ -38,14 +38,12 @@ import time import win32api +from simulation.config import load_config from THCI.IThci import IThci from THCI.OpenThread import OpenThreadTHCI, watched -from simulation.config import ( - REMOTE_USERNAME, - REMOTE_PASSWORD, - REMOTE_PORT, - REMOTE_OT_PATH, -) + +config = load_config() +ot_subpath = {item['tag']: item['subpath'] for item in config['ot_build']['ot']} class SSHHandle(object): @@ -133,21 +131,15 @@ def log(self, fmt, *args): class OpenThread_Sim(OpenThreadTHCI, IThci): __handle = None - # Do not use `os.path.join` as it uses backslash as the separator on Windows - device = REMOTE_OT_PATH + '/build/simulation/examples/apps/cli/ot-cli-ftd' - @watched def _connect(self): self.__lines = [] # Only actually connect once. if self.__handle is None: - assert self.connectType == 'ip' - assert '@' in self.telnetIp self.log('SSH connecting ...') - node_id, ssh_ip = self.telnetIp.split('@') - self.__handle = SSHHandle(ssh_ip, self.telnetPort, self.telnetUsername, self.telnetPassword, self.device, - node_id) + self.__handle = SSHHandle(self.ssh_ip, self.telnetPort, self.telnetUsername, self.telnetPassword, + self.device, self.node_id) self.log('connected to %s successfully', self.telnetIp) @@ -161,15 +153,23 @@ def _parseConnectionParams(self, params): if '@' not in discovery_add: raise ValueError('%r in the field `add` is invalid' % discovery_add) - node_id, ssh_ip = discovery_add.split('@') + prefix, self.ssh_ip = discovery_add.split('@') + self.tag, self.node_id = prefix.split('_') + self.node_id = int(self.node_id) # Let it crash if it is an invalid IP address - ipaddress.ip_address(ssh_ip) + ipaddress.ip_address(self.ssh_ip) + + # Do not use `os.path.join` as it uses backslash as the separator on Windows + global config + self.device = '/'.join([config['ot_path'], ot_subpath[self.tag], 'examples/apps/cli/ot-cli-ftd']) self.connectType = 'ip' self.telnetIp = self.port = discovery_add - self.telnetPort = REMOTE_PORT - self.telnetUsername = REMOTE_USERNAME - self.telnetPassword = REMOTE_PASSWORD + + ssh = config['ssh'] + self.telnetPort = ssh['port'] + self.telnetUsername = ssh['username'] + self.telnetPassword = ssh['password'] def _cliReadLine(self): if len(self.__lines) > 1: diff --git a/tools/harness-simulation/harness/Thread_Harness/simulation/config.py b/tools/harness-simulation/harness/Thread_Harness/simulation/config.py index de24adacc07..61bc379c7eb 100644 --- a/tools/harness-simulation/harness/Thread_Harness/simulation/config.py +++ b/tools/harness-simulation/harness/Thread_Harness/simulation/config.py @@ -27,8 +27,13 @@ # POSSIBILITY OF SUCH DAMAGE. # -REMOTE_USERNAME = 'pi' -REMOTE_PASSWORD = 'raspberry' -REMOTE_PORT = 22 +import os +import yaml -REMOTE_OT_PATH = '/home/pi/repo/openthread' +CONFIG_PATH = r'%s\GRL\Thread1.2\Thread_Harness\simulation\config.yml' % os.environ['systemdrive'] + + +def load_config(): + with open(CONFIG_PATH, 'rt') as f: + config = yaml.safe_load(f) + return config diff --git a/tools/harness-simulation/posix/build_docker_image.sh b/tools/harness-simulation/harness/Web/data/updateDeviceFields.py old mode 100755 new mode 100644 similarity index 52% rename from tools/harness-simulation/posix/build_docker_image.sh rename to tools/harness-simulation/harness/Web/data/updateDeviceFields.py index 634b04f1513..a6830022dad --- a/tools/harness-simulation/posix/build_docker_image.sh +++ b/tools/harness-simulation/harness/Web/data/updateDeviceFields.py @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env python # # Copyright (c) 2022, The OpenThread Authors. # All rights reserved. @@ -27,48 +27,32 @@ # POSSIBILITY OF SUCH DAMAGE. # -# NOTE: This script has not been fully tested up to now +# This script merges the device fields in the argument file to `deviceInputFields.xml` in Harness +# If a device field appears in both files, it will only keep that in the argument file -set -euxo pipefail +import os +import sys +import xml.etree.ElementTree as ET -OT_PATH=${OT_PATH:-"/home/pi/repo/openthread"} -OTBR_DOCKER_IMAGE=${OTBR_DOCKER_IMAGE:-"otbr-reference-device-1.2"} +HARNESS_XML_PATH = r'%s\GRL\Thread1.2\Web\data\deviceInputFields.xml' % os.environ['systemdrive'] -DOCKER_BUILD_OTBR_OPTIONS=( - "-DOTBR_DUA_ROUTING=ON" - "-DOT_DUA=ON" - "-DOT_MLR=ON" - "-DOT_THREAD_VERSION=1.2" - "-DOT_SIMULATION_MAX_NETWORK_SIZE=64" -) -git clone https://github.com/openthread/ot-br-posix.git --recurse-submodules --shallow-submodules --depth=1 -ETC_PATH="${OT_PATH}/tools/harness-simulation/posix/etc" +def main(): + tree = ET.parse(HARNESS_XML_PATH) + root = tree.getroot() + added = ET.parse(sys.argv[1]).getroot() -( - cd ot-br-posix - # Use system V `service` command instead - mkdir -p root/etc/init.d - cp "${ETC_PATH}/commissionerd" root/etc/init.d/commissionerd - sudo chown root:root root/etc/init.d/commissionerd - sudo chmod +x root/etc/init.d/commissionerd + added_names = set(device.attrib['name'] for device in added.iter('DEVICE')) + # If some devices already exist, remove them first, and then add them back in case of update + removed_devices = filter(lambda x: x.attrib['name'] in added_names, root.iter('DEVICE')) - cp "${ETC_PATH}/server.patch" script/server.patch - patch script/server script/server.patch - mkdir -p root/tmp - cp "${ETC_PATH}/requirements.txt" root/tmp/requirements.txt + for device in removed_devices: + root.remove(device) + for device in added.iter('DEVICE'): + root.append(device) - docker build . \ - -t "${OTBR_DOCKER_IMAGE}" \ - -f "${ETC_PATH}/Dockerfile" \ - --build-arg REFERENCE_DEVICE=1 \ - --build-arg BORDER_ROUTING=0 \ - --build-arg BACKBONE_ROUTER=1 \ - --build-arg NAT64=0 \ - --build-arg WEB_GUI=0 \ - --build-arg REST_API=0 \ - --build-arg EXTERNAL_COMMISSIONER=1 \ - --build-arg OTBR_OPTIONS="${DOCKER_BUILD_OTBR_OPTIONS[*]}" -) + tree.write(HARNESS_XML_PATH) -rm -rf ot-br-posix + +if __name__ == '__main__': + main() diff --git a/tools/harness-simulation/harness/install.bat b/tools/harness-simulation/harness/install.bat index bcf81e5089e..99be41c388c 100644 --- a/tools/harness-simulation/harness/install.bat +++ b/tools/harness-simulation/harness/install.bat @@ -25,18 +25,21 @@ :: POSSIBILITY OF SUCH DAMAGE. :: -xcopy /E /Y Thread_Harness %systemdrive%\GRL\Thread1.2\Thread_Harness -copy /Y ..\..\harness-thci\OpenThread.py %systemdrive%\GRL\Thread1.2\Thread_Harness\THCI -copy /Y ..\..\harness-thci\OpenThread_BR.py %systemdrive%\GRL\Thread1.2\Thread_Harness\THCI -copy /Y ..\..\harness-thci\OpenThread.png %systemdrive%\GRL\Thread1.2\Web\images -copy /Y ..\..\harness-thci\OpenThread_BR.png %systemdrive%\GRL\Thread1.2\Web\images -xcopy /E /Y ..\posix\sniffer_sim\proto %systemdrive%\GRL\Thread1.2\Thread_Harness\simulation\Sniffer\proto +set THREADDIR=%systemdrive%\GRL\Thread1.2 +xcopy /E /Y Thread_Harness %THREADDIR%\Thread_Harness +copy /Y ..\..\harness-thci\OpenThread.py %THREADDIR%\Thread_Harness\THCI +copy /Y ..\..\harness-thci\OpenThread_BR.py %THREADDIR%\Thread_Harness\THCI +copy /Y ..\..\harness-thci\OpenThread.png %THREADDIR%\Web\images +copy /Y ..\..\harness-thci\OpenThread_BR.png %THREADDIR%\Web\images +copy /Y ..\posix\config.yml %THREADDIR%\Thread_Harness\simulation +xcopy /E /Y ..\posix\sniffer_sim\proto %THREADDIR%\Thread_Harness\simulation\Sniffer\proto -%systemdrive%\GRL\Thread1.2\Python27\python.exe -m pip install --upgrade pip -%systemdrive%\GRL\Thread1.2\Python27\python.exe -m pip install -r requirements.txt +%THREADDIR%\Python27\python.exe -m pip install --upgrade pip +%THREADDIR%\Python27\python.exe -m pip install -r requirements.txt -set BASEDIR=%systemdrive%\GRL\Thread1.2\Thread_Harness +%THREADDIR%\Python27\python.exe Web\data\updateDeviceFields.py Web\data\deviceInputFields.xml +set BASEDIR=%THREADDIR%\Thread_Harness %systemdrive%\GRL\Thread1.2\Python27\python.exe -m grpc_tools.protoc -I%BASEDIR% --python_out=%BASEDIR% --grpc_python_out=%BASEDIR% simulation/Sniffer/proto/sniffer.proto pause diff --git a/tools/harness-simulation/harness/requirements.txt b/tools/harness-simulation/harness/requirements.txt index 0d1d827d987..179c044ca44 100644 --- a/tools/harness-simulation/harness/requirements.txt +++ b/tools/harness-simulation/harness/requirements.txt @@ -1,2 +1,3 @@ grpcio==1.20.1 grpcio-tools==1.20.1 +PyYAML==5.4.1 diff --git a/tools/harness-simulation/posix/config.yml b/tools/harness-simulation/posix/config.yml new file mode 100644 index 00000000000..11b29bca545 --- /dev/null +++ b/tools/harness-simulation/posix/config.yml @@ -0,0 +1,91 @@ +ot_path: "/home/pi/repo/openthread" +ot_build: + max_number: 64 + ot: + - tag: OT11 + version: '1.1' + number: 33 + subpath: build/ot11/simulation + cflags: + - "-DOPENTHREAD_CONFIG_IP6_MAX_EXT_MCAST_ADDRS=8" + options: + - "-DOT_REFERENCE_DEVICE=ON" + - "-DOT_COMMISSIONER=ON" + - "-DOT_JOINER=ON" + - tag: OT12 + version: '1.2' + number: 10 + subpath: build/ot12/simulation + cflags: + - "-DOPENTHREAD_CONFIG_IP6_MAX_EXT_MCAST_ADDRS=8" + options: + - "-DOT_REFERENCE_DEVICE=ON" + - "-DOT_DUA=ON" + - "-DOT_MLR=ON" + - "-DOT_COMMISSIONER=ON" + - "-DOT_JOINER=ON" + - "-DOT_CSL_RECEIVER=ON" + - "-DOT_LINK_METRICS_SUBJECT=ON" + - "-DOT_LINK_METRICS_INITIATOR=ON" + - tag: OT13 + version: '1.3' + number: 10 + subpath: build/ot13/simulation + cflags: + - "-DOPENTHREAD_CONFIG_IP6_MAX_EXT_MCAST_ADDRS=8" + options: + - "-DOT_REFERENCE_DEVICE=ON" + - "-DOT_DUA=ON" + - "-DOT_MLR=ON" + - "-DOT_COMMISSIONER=ON" + - "-DOT_JOINER=ON" + - "-DOT_CSL_RECEIVER=ON" + - "-DOT_LINK_METRICS_SUBJECT=ON" + - "-DOT_LINK_METRICS_INITIATOR=ON" + otbr: + - tag: OTBR12 + version: '1.2' + number: 4 + docker_image: otbr-reference-device-1.2 + build_args: + - REFERENCE_DEVICE=1 + - BORDER_ROUTING=0 + - BACKBONE_ROUTER=1 + - NAT64=0 + - WEB_GUI=0 + - REST_API=0 + - OT_COMMISSIONER=1 + options: + - "-DOTBR_DUA_ROUTING=ON" + - "-DOT_DUA=ON" + - "-DOT_MLR=ON" + rcp_subpath: build/ot12/simulation + rcp_options: + - "-DOT_LINK_METRICS_SUBJECT=ON" + - tag: OTBR13 + version: '1.3' + number: 4 + docker_image: otbr-reference-device-1.3 + build_args: + - REFERENCE_DEVICE=1 + - BORDER_ROUTING=1 + - BACKBONE_ROUTER=1 + - NAT64=0 + - WEB_GUI=0 + - REST_API=0 + - EXTERNAL_COMMISSIONER=1 + options: + - "-DOTBR_DUA_ROUTING=ON" + - "-DOT_DUA=ON" + - "-DOT_MLR=ON" + rcp_subpath: build/ot13/simulation + rcp_options: + - "-DOT_LINK_METRICS_SUBJECT=ON" +ssh: + username: pi + password: raspberry + port: 22 +sniffer: + number: 2 + server_port_base: 50051 +discovery_ifname: eth0 diff --git a/tools/harness-simulation/posix/install.sh b/tools/harness-simulation/posix/install.sh index f82fbbd8bf0..aedba39166d 100755 --- a/tools/harness-simulation/posix/install.sh +++ b/tools/harness-simulation/posix/install.sh @@ -29,9 +29,121 @@ set -euxo pipefail -BASE_DIR=$(dirname "$0") -SNIFFER_DIR="${BASE_DIR}/sniffer_sim" +POSIX_DIR="$(cd "$(dirname "$0")" && pwd)" +OT_DIR="${POSIX_DIR}/../../.." +ETC_DIR="${POSIX_DIR}/etc" +SNIFFER_DIR="${POSIX_DIR}/sniffer_sim" -pip3 install -r "${BASE_DIR}/requirements.txt" +PACKAGES=( + "docker.io" + "git" + "jq" + "socat" + "tshark" +) +sudo apt install -y "${PACKAGES[@]}" + +pip3 install -r "${POSIX_DIR}/requirements.txt" python3 -m grpc_tools.protoc -I"${SNIFFER_DIR}" --python_out="${SNIFFER_DIR}" --grpc_python_out="${SNIFFER_DIR}" proto/sniffer.proto + +CONFIG_NAME=${1:-"${POSIX_DIR}/config.yml"} +# convert YAML to JSON +CONFIG=$(python3 -c 'import json, sys, yaml; print(json.dumps(yaml.safe_load(open(sys.argv[1]))))' "$CONFIG_NAME") + +MAX_NETWORK_SIZE=$(jq -r '.ot_build.max_number' <<<"$CONFIG") + +build_ot() +{ + # SC2155: Declare and assign separately to avoid masking return values + local target build_dir cflags version options + target="ot-cli-ftd" + build_dir=$(jq -r '.subpath' <<<"$1") + cflags=$(jq -r '.cflags | join(" ")' <<<"$1") + version=$(jq -r '.version' <<<"$1") + options=$(jq -r '.options | join(" ")' <<<"$1") + # Intended splitting of options + read -ra options <<<"$options" + + ( + cd "$OT_DIR" + + OT_CMAKE_NINJA_TARGET="$target" \ + OT_CMAKE_BUILD_DIR="$build_dir" \ + CFLAGS="$cflags" \ + CXXFLAGS="$cflags" \ + script/cmake-build \ + simulation \ + "${options[@]}" \ + -DOT_THREAD_VERSION="$version" \ + -DOT_SIMULATION_MAX_NETWORK_SIZE="$MAX_NETWORK_SIZE" + ) +} + +build_otbr() +{ + # SC2155: Declare and assign separately to avoid masking return values + local target build_dir version rcp_options + target="ot-rcp" + build_dir=$(jq -r '.rcp_subpath' <<<"$1") + version=$(jq -r '.version' <<<"$1") + rcp_options=$(jq -r '.rcp_options | join(" ")' <<<"$1") + # Intended splitting of rcp_options + read -ra rcp_options <<<"$rcp_options" + + ( + cd "$OT_DIR" + + OT_CMAKE_NINJA_TARGET="$target" \ + OT_CMAKE_BUILD_DIR="$build_dir" \ + script/cmake-build \ + simulation \ + "${rcp_options[@]}" \ + -DOT_THREAD_VERSION="$version" \ + -DOT_SIMULATION_MAX_NETWORK_SIZE="$MAX_NETWORK_SIZE" + ) + + # SC2155: Declare and assign separately to avoid masking return values + local otbr_docker_image build_args options + otbr_docker_image=$(jq -r '.docker_image' <<<"$1") + build_args=$(jq -r '.build_args | map("--build-arg " + .) | join(" ")' <<<"$1") + # Intended splitting of build_args + read -ra build_args <<<"$build_args" + options=$(jq -r '.options | join(" ")' <<<"$1") + + local otbr_options=( + "$options" + "-DOT_THREAD_VERSION=$version" + "-DOT_SIMULATION_MAX_NETWORK_SIZE=$MAX_NETWORK_SIZE" + ) + + docker build . \ + -t "${otbr_docker_image}" \ + -f "${ETC_DIR}/Dockerfile" \ + "${build_args[@]}" \ + --build-arg OTBR_OPTIONS="${otbr_options[*]}" +} + +for item in $(jq -c '.ot_build.ot | .[]' <<<"$CONFIG"); do + build_ot "$item" +done + +git clone https://github.com/openthread/ot-br-posix.git --recurse-submodules --shallow-submodules --depth=1 +( + cd ot-br-posix + # Use system V `service` command instead + mkdir -p root/etc/init.d + cp "${ETC_DIR}/commissionerd" root/etc/init.d/commissionerd + sudo chown root:root root/etc/init.d/commissionerd + sudo chmod +x root/etc/init.d/commissionerd + + cp "${ETC_DIR}/server.patch" script/server.patch + patch script/server script/server.patch + mkdir -p root/tmp + cp "${ETC_DIR}/requirements.txt" root/tmp/requirements.txt + + for item in $(jq -c '.ot_build.otbr | .[]' <<<"$CONFIG"); do + build_otbr "$item" + done +) +rm -rf ot-br-posix diff --git a/tools/harness-simulation/posix/launch_testbed.py b/tools/harness-simulation/posix/launch_testbed.py old mode 100644 new mode 100755 index 0038135d395..5ca2537eaf5 --- a/tools/harness-simulation/posix/launch_testbed.py +++ b/tools/harness-simulation/posix/launch_testbed.py @@ -40,13 +40,8 @@ import subprocess import sys from typing import Iterable +import yaml -from config import ( - MAX_NODES_NUM, - MAX_SNIFFER_NUM, - SNIFFER_SERVER_PORT_BASE, - OTBR_DOCKER_NAME_PREFIX, -) from otbr_sim import otbr_docker GROUP = 'ff02::114' @@ -94,34 +89,41 @@ def _advertise(s: socket.socket, dst, info): s.sendto(json.dumps(info).encode('utf-8'), dst) -def advertise_devices(s: socket.socket, dst, ven: str, add: str, nodeids: Iterable[int], prefix: str = ''): +def advertise_devices(s: socket.socket, dst, ven: str, add: str, nodeids: Iterable[int], tag: str): for nodeid in nodeids: info = { 'ven': ven, 'mod': 'OpenThread', 'ver': '4', - 'add': f'{prefix}{nodeid}@{add}', + 'add': f'{tag}_{nodeid}@{add}', 'por': 22, } _advertise(s, dst, info) -def advertise_sniffers(s: socket.socket, dst, add: str, number: int): - for i in range(number): +def advertise_sniffers(s: socket.socket, dst, add: str, ports: Iterable[int]): + for port in ports: info = { 'add': add, - 'por': i + SNIFFER_SERVER_PORT_BASE, + 'por': port, } _advertise(s, dst, info) -def start_sniffer(addr: str, port: int) -> subprocess.Popen: +def start_sniffer(addr: str, port: int, ot_path: str, max_nodes_num: int) -> subprocess.Popen: if isinstance(ipaddress.ip_address(addr), ipaddress.IPv6Address): server = f'[{addr}]:{port}' else: server = f'{addr}:{port}' - cmd = ['python3', 'sniffer_sim/sniffer.py', '--grpc-server', server] + cmd = [ + 'python3', + os.path.join(ot_path, 'tools/harness-simulation/posix/sniffer_sim/sniffer.py'), + '--grpc-server', + server, + '--max-nodes-num', + str(max_nodes_num), + ] logging.info('Executing command: %s', ' '.join(cmd)) return subprocess.Popen(cmd) @@ -131,79 +133,74 @@ def main(): # Parse arguments parser = argparse.ArgumentParser() - - # Determine the interface - parser.add_argument('-i', - '--interface', - dest='ifname', + parser.add_argument('-c', + '--config', + dest='config', type=str, required=True, - help='the interface used for discovery') - - # Determine the number of OpenThread FTD simulations to be "detected" and then started - parser.add_argument('--ot', - dest='ot_num', - type=int, - required=False, - default=0, - help=f'the number of OpenThread FTD simulations') - - # Determine the number of OpenThread BR simulations to be initiated and then detected - parser.add_argument('--otbr', - dest='otbr_num', - type=int, - required=False, - default=0, - help=f'the number of OpenThread BR simulations') - - # Determine the number of sniffer simulations to be started and then detected - parser.add_argument('--sniffer', - dest='sniffer_num', - type=int, - required=False, - default=0, - help=f'the number of sniffer simulations, no more than {MAX_SNIFFER_NUM}') - + help='the path of the configuration JSON file') args = parser.parse_args() + with open(args.config, 'rt') as f: + config = yaml.safe_load(f) - # Check validation of arguments - if args.ot_num < 0: - raise ValueError(f'The number of FTDs should be non-negative') + ot_path = config['ot_path'] + ot_build = config['ot_build'] + max_nodes_num = ot_build['max_number'] + # No test case requires more than 2 sniffers + MAX_SNIFFER_NUM = 2 - if args.otbr_num < 0: - raise ValueError(f'The number of OTBRs should be non-negative') + ot_devices = [(item['tag'], item['number']) for item in ot_build['ot']] + otbr_devices = [(item['tag'], item['number']) for item in ot_build['otbr']] + ot_nodes_num = sum(x[1] for x in ot_devices) + otbr_nodes_num = sum(x[1] for x in otbr_devices) + nodes_num = ot_nodes_num + otbr_nodes_num + sniffer_num = config['sniffer']['number'] - if not 0 <= args.sniffer_num <= MAX_SNIFFER_NUM: - raise ValueError(f'The number of sniffers should be between 0 and {MAX_SNIFFER_NUM}') + # Check validation of numbers + if not all(0 <= x[1] <= max_nodes_num for x in ot_devices): + raise ValueError(f'The number of devices of each OT version should be between 0 and {max_nodes_num}') - if args.ot_num == args.otbr_num == 0: - raise ValueError('At least one device is required') + if not all(0 <= x[1] <= max_nodes_num for x in otbr_devices): + raise ValueError(f'The number of devices of each OTBR version should be between 0 and {max_nodes_num}') - if args.sniffer_num == 0: - raise ValueError('At least one sniffer is required') + if not 1 <= nodes_num <= max_nodes_num: + raise ValueError(f'The number of devices should be between 1 and {max_nodes_num}') - if args.ot_num + args.otbr_num > MAX_NODES_NUM: - raise ValueError(f'The number of all devices should be no more than {MAX_NODES_NUM}') + if not 1 <= sniffer_num <= MAX_SNIFFER_NUM: + raise ValueError(f'The number of sniffers should be between 1 and {MAX_SNIFFER_NUM}') # Get the local IP address on the specified interface - addr = get_ipaddr(args.ifname) + ifname = config['discovery_ifname'] + addr = get_ipaddr(ifname) # Start the sniffer + sniffer_server_port_base = config['sniffer']['server_port_base'] sniffer_procs = [] - for i in range(args.sniffer_num): - sniffer_procs.append(start_sniffer(addr, i + SNIFFER_SERVER_PORT_BASE)) + for i in range(sniffer_num): + sniffer_procs.append(start_sniffer(addr, i + sniffer_server_port_base, ot_path, max_nodes_num)) # OTBR firewall scripts create rules inside the Docker container # Run modprobe to load the kernel modules for iptables subprocess.run(['sudo', 'modprobe', 'ip6table_filter']) # Start the BRs otbr_dockers = [] - for nodeid in range(args.ot_num + 1, args.ot_num + args.otbr_num + 1): - otbr_dockers.append(otbr_docker.OtbrDocker(nodeid, OTBR_DOCKER_NAME_PREFIX + str(nodeid))) - - s = init_socket(args.ifname, GROUP, PORT) - - logging.info('Advertising on interface %s group %s ...', args.ifname, GROUP) + nodeid = ot_nodes_num + for item in ot_build['otbr']: + tag = item['tag'] + ot_rcp_path = os.path.join(ot_path, item['rcp_subpath'], 'examples/apps/ncp/ot-rcp') + docker_image = item['docker_image'] + for _ in range(item['number']): + nodeid += 1 + otbr_dockers.append( + otbr_docker.OtbrDocker(nodeid=nodeid, + ot_path=ot_path, + ot_rcp_path=ot_rcp_path, + docker_image=docker_image, + docker_name=f'{tag}_{nodeid}')) + + s = init_socket(ifname, GROUP, PORT) + + logging.info('Advertising on interface %s group %s ...', ifname, GROUP) # Terminate all sniffer simulation server processes and then exit def exit_handler(signum, context): @@ -227,17 +224,19 @@ def exit_handler(signum, context): if data == b'BBR': logging.info('Received OpenThread simulation query, advertising') - advertise_devices(s, src, ven='OpenThread_Sim', add=addr, nodeids=range(1, args.ot_num + 1)) - advertise_devices(s, - src, - ven='OpenThread_BR_Sim', - add=addr, - nodeids=range(args.ot_num + 1, args.ot_num + args.otbr_num + 1), - prefix=OTBR_DOCKER_NAME_PREFIX) + + nodeid = 1 + for ven, devices in [('OpenThread_Sim', ot_devices), ('OpenThread_BR_Sim', otbr_devices)]: + for tag, number in devices: + advertise_devices(s, src, ven=ven, add=addr, nodeids=range(nodeid, nodeid + number), tag=tag) + nodeid += number elif data == b'Sniffer': logging.info('Received sniffer simulation query, advertising') - advertise_sniffers(s, src, add=addr, number=args.sniffer_num) + advertise_sniffers(s, + src, + add=addr, + ports=range(sniffer_server_port_base, sniffer_server_port_base + sniffer_num)) else: logging.warning('Received %r, but ignored', data) diff --git a/tools/harness-simulation/posix/otbr_sim/otbr_docker.py b/tools/harness-simulation/posix/otbr_sim/otbr_docker.py index b116163f7cb..cc7f73b47fc 100644 --- a/tools/harness-simulation/posix/otbr_sim/otbr_docker.py +++ b/tools/harness-simulation/posix/otbr_sim/otbr_docker.py @@ -33,14 +33,15 @@ import subprocess import time -from config import (OT_PATH, OTBR_DOCKER_IMAGE) - class OtbrDocker: device_pattern = re.compile('(?<=PTY is )/dev/.+$') - def __init__(self, nodeid: int, docker_name: str): + def __init__(self, nodeid: int, ot_path: str, ot_rcp_path: str, docker_image: str, docker_name: str): self.nodeid = nodeid + self.ot_path = ot_path + self.ot_rcp_path = ot_rcp_path + self.docker_image = docker_image self.docker_name = docker_name self.logger = logging.getLogger('otbr_docker.OtbrDocker') @@ -96,9 +97,8 @@ def _shutdown_socat(self): self._rcp_device = None def _launch_ot_rcp(self): - ot_rcp_path = os.path.join(OT_PATH, 'build/simulation/examples/apps/ncp/ot-rcp') self._ot_rcp_proc = subprocess.Popen( - f'{ot_rcp_path} {self.nodeid} > {self._rcp_device_pty} < {self._rcp_device_pty}', + f'{self.ot_rcp_path} {self.nodeid} > {self._rcp_device_pty} < {self._rcp_device_pty}', shell=True, stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL, @@ -136,8 +136,8 @@ def _launch_docker(self): '-v', f'{self._rcp_device}:/dev/ttyUSB0', '-v', - f'{OT_PATH.rstrip("/")}:/home/pi/repo/openthread', - OTBR_DOCKER_IMAGE, + f'{self.ot_path.rstrip("/")}:/home/pi/repo/openthread', + self.docker_image, ] self.logger.info('Launching docker: %s', ' '.join(cmd)) launch_proc = subprocess.Popen(cmd, diff --git a/tools/harness-simulation/posix/requirements.txt b/tools/harness-simulation/posix/requirements.txt index a6646e7b950..21ed00b551f 100644 --- a/tools/harness-simulation/posix/requirements.txt +++ b/tools/harness-simulation/posix/requirements.txt @@ -1,2 +1,3 @@ grpcio grpcio-tools +PyYAML diff --git a/tools/harness-simulation/posix/sniffer_sim/sniffer.py b/tools/harness-simulation/posix/sniffer_sim/sniffer.py index 2e4bc8e8958..b059cf3da07 100644 --- a/tools/harness-simulation/posix/sniffer_sim/sniffer.py +++ b/tools/harness-simulation/posix/sniffer_sim/sniffer.py @@ -58,21 +58,21 @@ class SnifferServicer(sniffer_pb2_grpc.Sniffer): RECV_BUFFER_SIZE = 4096 TIMEOUT = 0.1 - MAX_NODES_NUM = 64 def _reset(self): self._state = CaptureState.NONE self._pcap = None - self._allowed_nodeids = None + self._denied_nodeids = None self._transport = None self._thread = None self._thread_alive.clear() self._pcapng_filename = None self._tshark_proc = None - def __init__(self): + def __init__(self, max_nodes_num): + self._max_nodes_num = max_nodes_num self._thread_alive = threading.Event() - self._mutex = threading.Lock() # for self._allowed_nodeids + self._mutex = threading.Lock() # for self._denied_nodeids self._reset() def Start(self, request, context): @@ -104,8 +104,7 @@ def Start(self, request, context): self._pcap = pcap_codec.PcapCodec(request.channel, fifo_name) # Sniffer all nodes in default, i.e. there is no RF enclosure - # In this case, self._allowed_nodeids is set to None - self._allowed_nodeids = None + self._denied_nodeids = set() # Create transport transport_factory = sniffer_transport.SnifferTransportFactory() @@ -130,10 +129,10 @@ def _sniffer_main_loop(self): continue with self._mutex: - allowed_nodeids = self._allowed_nodeids + denied_nodeids = self._denied_nodeids # Equivalent to RF enclosure - if allowed_nodeids is None or nodeid in allowed_nodeids: + if nodeid not in denied_nodeids: self._pcap.append(data) def FilterNodes(self, request, context): @@ -145,14 +144,14 @@ def FilterNodes(self, request, context): if not (self._state & CaptureState.THREAD): return sniffer_pb2.FilterNodesResponse(status=sniffer_pb2.OPERATION_ERROR) - allowed_nodeids = set(request.nodeids) + denied_nodeids = set(request.nodeids) # Validate the node IDs - for nodeid in allowed_nodeids: - if not 1 <= nodeid <= self.MAX_NODES_NUM: + for nodeid in denied_nodeids: + if not 1 <= nodeid <= self._max_nodes_num: return sniffer_pb2.FilterNodesResponse(status=sniffer_pb2.VALUE_ERROR) with self._mutex: - self._allowed_nodeids = allowed_nodeids + self._denied_nodeids = denied_nodeids return sniffer_pb2.FilterNodesResponse(status=sniffer_pb2.OK) @@ -182,9 +181,9 @@ def Stop(self, request, context): return sniffer_pb2.StopResponse(status=sniffer_pb2.OK, pcap_content=pcap_content) -def serve(address_port): +def serve(address_port, max_nodes_num): server = grpc.server(futures.ThreadPoolExecutor(max_workers=1)) - sniffer_pb2_grpc.add_SnifferServicer_to_server(SnifferServicer(), server) + sniffer_pb2_grpc.add_SnifferServicer_to_server(SnifferServicer(max_nodes_num), server) # add_secure_port requires a web domain server.add_insecure_port(address_port) logging.info('server starts on %s', address_port) @@ -208,9 +207,14 @@ def run_sniffer(): type=str, required=True, help='the address of the sniffer server') + parser.add_argument('--max-nodes-num', + dest='max_nodes_num', + type=int, + required=True, + help='the maximum number of nodes') args = parser.parse_args() - serve(args.grpc_server) + serve(args.grpc_server, args.max_nodes_num) if __name__ == '__main__': diff --git a/tools/harness-thci/OpenThread.py b/tools/harness-thci/OpenThread.py index 8d65cc464df..db145ab5a2e 100644 --- a/tools/harness-thci/OpenThread.py +++ b/tools/harness-thci/OpenThread.py @@ -1248,13 +1248,7 @@ def getParentAddress(self): @API def powerDown(self): """power down the Thread device""" - self.__sendCommand('reset', expectEcho=False) - - if not self.IsBorderRouter: - self._disconnect() - self._connect() - - self.isPowerDown = True + self._reset() @API def powerUp(self): @@ -1266,7 +1260,8 @@ def powerUp(self): self.__setPollPeriod(self.__sedPollPeriod) self.__startOpenThread() - def reset_and_wait_for_connection(self, timeout=3): + @watched + def _reset(self, timeout=3): print("Waiting after reset timeout: {} s".format(timeout)) start_time = time.time() self.__sendCommand('reset', expectEcho=False) @@ -1285,6 +1280,8 @@ def reset_and_wait_for_connection(self, timeout=3): else: raise AssertionError("Could not connect with OT device {} after reset.".format(self)) + def reset_and_wait_for_connection(self, timeout=3): + self._reset(timeout=timeout) if self.deviceRole == Thread_Device_Role.SED: self.__setPollPeriod(self.__sedPollPeriod) @@ -3259,6 +3256,16 @@ def setCcmState(self, state=0): def setVrCheckSkip(self): self.__executeCommand("tvcheck disable") + @API + def addBlockedNodeId(self, node_id): + cmd = 'nodeidfilter deny %d' % node_id + self.__executeCommand(cmd) + + @API + def clearBlockedNodeIds(self): + cmd = 'nodeidfilter clear' + self.__executeCommand(cmd) + class OpenThread(OpenThreadTHCI, IThci): diff --git a/tools/harness-thci/OpenThread_BR.py b/tools/harness-thci/OpenThread_BR.py index 0764fec32ba..a76ca356f71 100644 --- a/tools/harness-thci/OpenThread_BR.py +++ b/tools/harness-thci/OpenThread_BR.py @@ -636,7 +636,8 @@ def __truncateSyslog(self): self.bash('truncate -s 0 /var/log/syslog') def __dumpSyslog(self): - output = self.bash_unwatched('grep "otbr-agent" /var/log/syslog') + cmd = self.extraParams.get('cmd-dump-otbr-log', 'grep "otbr-agent" /var/log/syslog') + output = self.bash_unwatched(cmd) for line in output: self.log('%s', line) @@ -648,28 +649,22 @@ def get_eth_addrs(self): @API def mdns_query(self, service='_meshcop._udp.local', addrs_allowlist=(), addrs_denylist=()): - cleanup = [] - - def ip6tables_cmd(cmd): - assert '-I INPUT' in cmd - self.bash(cmd) - cleanup.append(cmd.replace('-I INPUT', '-D INPUT')) - try: for deny_addr in addrs_denylist: - ip6tables_cmd('ip6tables -I INPUT -p udp --dport 5353 -s %s -j DROP' % deny_addr) + self.bash('ip6tables -A INPUT -p udp --dport 5353 -s %s -j DROP' % deny_addr) if addrs_allowlist: for allow_addr in addrs_allowlist: - ip6tables_cmd('ip6tables -I INPUT -p udp --dport 5353 -s %s -j ACCEPT' % allow_addr) + self.bash('ip6tables -A INPUT -p udp --dport 5353 -s %s -j ACCEPT' % allow_addr) - ip6tables_cmd('ip6tables -I INPUT -p udp --dport 5353 -j DROP') + self.bash('ip6tables -A INPUT -p udp --dport 5353 -j DROP') return self._mdns_query_impl(service, find_active=(addrs_allowlist or addrs_denylist)) finally: - for cmd in cleanup: - self.bash(cmd) + self.bash('ip6tables -L INPUT -v') + self.bash('ip6tables -F INPUT') + time.sleep(1) def _mdns_query_impl(self, service, find_active): # For BBR-TC-03 or DH test cases (empty arguments) just send a query diff --git a/examples/platforms/cc2538/Makefile.platform.am b/zephyr/module.yml similarity index 78% rename from examples/platforms/cc2538/Makefile.platform.am rename to zephyr/module.yml index f908b4ed863..75f79ec19f0 100644 --- a/examples/platforms/cc2538/Makefile.platform.am +++ b/zephyr/module.yml @@ -1,5 +1,5 @@ # -# Copyright (c) 2017, The OpenThread Authors. +# Copyright (c) 2022, The OpenThread Authors. # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -26,14 +26,6 @@ # POSSIBILITY OF SUCH DAMAGE. # -# -# cc2538 platform-specific Makefile -# - -LDADD_COMMON += \ - $(top_builddir)/examples/platforms/cc2538/libopenthread-cc2538.a \ - $(NULL) - -LDFLAGS_COMMON += \ - -T $(top_srcdir)/examples/platforms/cc2538/cc2538.ld \ - $(NULL) +build: + cmake-ext: True + kconfig-ext: True