diff --git a/.travis.yml b/.travis.yml index 050dc85a5..944fcbbb4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -47,6 +47,12 @@ after_success: matrix: include: + - env: BUILD_TARGET="posix-app-cli" VERBOSE=1 + os: linux + compiler: gcc + - env: BUILD_TARGET="posix-app-ncp" VERBOSE=1 + os: linux + compiler: gcc - env: BUILD_TARGET="posix-distcheck" CC=clang-5.0 CXX=clang++-5.0 VERBOSE=1 VIRTUAL_TIME=1 os: linux addons: diff --git a/.travis/before_install.sh b/.travis/before_install.sh index 91ea54a17..3f8e3b5b5 100755 --- a/.travis/before_install.sh +++ b/.travis/before_install.sh @@ -39,10 +39,10 @@ cd /tmp || die [ $TRAVIS_OS_NAME != linux ] || { sudo apt-get update || die - [ $BUILD_TARGET != posix-distcheck -a $BUILD_TARGET != posix-32-bit -a $BUILD_TARGET != posix-mtd -a $BUILD_TARGET != posix-ncp ] || { + [ $BUILD_TARGET != posix-distcheck -a $BUILD_TARGET != posix-32-bit -a $BUILD_TARGET != posix-app-cli -a $BUILD_TARGET != posix-mtd -a $BUILD_TARGET != posix-ncp -a $BUILD_TARGET != posix-app-ncp ] || { pip install --user --upgrade pip || die pip install --user -r $TRAVIS_BUILD_DIR/tests/scripts/thread-cert/requirements.txt || die - [ $BUILD_TARGET != posix-ncp ] || { + [ $BUILD_TARGET != posix-ncp -a $BUILD_TARGET != posix-app-ncp ] || { # Packages used by ncp tools. pip install --user git+https://github.com/openthread/pyspinel || die } diff --git a/.travis/script.sh b/.travis/script.sh index 1daa35f51..bee1204e1 100755 --- a/.travis/script.sh +++ b/.travis/script.sh @@ -432,6 +432,13 @@ set -x COVERAGE=1 CFLAGS=-m32 CXXFLAGS=-m32 LDFLAGS=-m32 make -f examples/Makefile-posix check || die } +[ $BUILD_TARGET != posix-app-cli ] || { + ./bootstrap || die + make -f examples/Makefile-posix || die + make -f src/posix/Makefile-posix || die + OT_CLI_PATH="$(pwd)/$(ls output/posix/*/bin/ot-cli)" RADIO_DEVICE="$(pwd)/$(ls output/*/bin/ot-ncp-radio)" COVERAGE=1 make -f src/posix/Makefile-posix check || die +} + [ $BUILD_TARGET != posix-mtd ] || { ./bootstrap || die COVERAGE=1 CFLAGS=-m32 CXXFLAGS=-m32 LDFLAGS=-m32 USE_MTD=1 make -f examples/Makefile-posix check || die @@ -442,6 +449,13 @@ set -x make -f examples/Makefile-posix check configure_OPTIONS="--enable-ncp-app=ftd --with-ncp-bus=spi --with-examples=posix" || die } +[ $BUILD_TARGET != posix-app-ncp ] || { + ./bootstrap || die + make -f examples/Makefile-posix || die + make -f src/posix/Makefile-posix || die + OT_NCP_PATH="$(pwd)/$(ls output/posix/*/bin/ot-ncp)" RADIO_DEVICE="$(pwd)/$(ls output/*/bin/ot-ncp-radio)" COVERAGE=1 NODE_TYPE=ncp-sim make -f src/posix/Makefile-posix check || die +} + [ $BUILD_TARGET != posix-ncp ] || { ./bootstrap || die NODE_TYPE=ncp-sim make -f examples/Makefile-posix check || die diff --git a/Makefile.am b/Makefile.am index d1c66248e..a15b3bf3d 100644 --- a/Makefile.am +++ b/Makefile.am @@ -50,8 +50,8 @@ AM_DISTCHECK_CONFIGURE_FLAGS = \ SUBDIRS = \ include \ - src \ third_party \ + src \ examples \ tests \ tools \ diff --git a/configure.ac b/configure.ac index be4cf8878..147df1ddf 100644 --- a/configure.ac +++ b/configure.ac @@ -373,6 +373,59 @@ NL_ENABLE_TESTS([yes]) AM_CONDITIONAL([OPENTHREAD_BUILD_TESTS], [test "${nl_cv_build_tests}" = "yes"]) +# +# Posix App +# + +AC_ARG_ENABLE( + [posix-app], + [AS_HELP_STRING([--enable-posix-app],[Enable POSIX app(no|ncp|cli|all) @<:@default=no@:>@.])], + [ + case "${enableval}" in + all|both|yes) + enable_posix_app=yes + OPENTHREAD_ENABLE_POSIX_APP=1 + OPENTHREAD_ENABLE_POSIX_APP_NCP=1 + OPENTHREAD_ENABLE_POSIX_APP_CLI=1 + ;; + no) + enable_posix_app=no + OPENTHREAD_ENABLE_POSIX_APP=0 + OPENTHREAD_ENABLE_POSIX_APP_NCP=0 + OPENTHREAD_ENABLE_POSIX_APP_CLI=0 + ;; + ncp) + enable_posix_app=yes + OPENTHREAD_ENABLE_POSIX_APP=1 + OPENTHREAD_ENABLE_POSIX_APP_NCP=1 + OPENTHREAD_ENABLE_POSIX_APP_CLI=0 + ;; + cli) + enable_posix_app=yes + OPENTHREAD_ENABLE_POSIX_APP=1 + OPENTHREAD_ENABLE_POSIX_APP_NCP=0 + OPENTHREAD_ENABLE_POSIX_APP_CLI=1 + ;; + *) + AC_MSG_ERROR([Invalid value ${enableval} for --enable-posix-app]) + ;; + esac + ], + [ + enable_posix_app=no + OPENTHREAD_ENABLE_POSIX_APP=0 + OPENTHREAD_ENABLE_POSIX_APP_NCP=0 + OPENTHREAD_ENABLE_POSIX_APP_CLI=0]) + +AC_MSG_CHECKING([posix-app]) +AC_MSG_RESULT(${enable_posix_app}) +AM_CONDITIONAL([OPENTHREAD_ENABLE_POSIX_APP], [test "${OPENTHREAD_ENABLE_POSIX_APP}" == "1"]) +AM_CONDITIONAL([OPENTHREAD_ENABLE_POSIX_APP_NCP], [test "${OPENTHREAD_ENABLE_POSIX_APP_NCP}" == "1"]) +AM_CONDITIONAL([OPENTHREAD_ENABLE_POSIX_APP_CLI], [test "${OPENTHREAD_ENABLE_POSIX_APP_CLI}" == "1"]) +AC_SUBST(OPENTHREAD_ENABLE_POSIX_APP) +AC_SUBST(OPENTHREAD_ENABLE_POSIX_APP_NCP) +AC_SUBST(OPENTHREAD_ENABLE_POSIX_APP_CLI) + # # CLI Library # @@ -413,9 +466,9 @@ AC_ARG_ENABLE(cli-app, AC_MSG_CHECKING([cli-app modes]) AC_MSG_RESULT(${enable_cli_app}) -AM_CONDITIONAL([OPENTHREAD_ENABLE_CLI], [test "${enable_cli_app}" == "yes"]) +AM_CONDITIONAL([OPENTHREAD_ENABLE_CLI], [test "${enable_cli_app}" == "yes" -o "${OPENTHREAD_ENABLE_POSIX_APP_CLI}" == "1"]) AM_CONDITIONAL([OPENTHREAD_ENABLE_CLI_MTD], [test "${enable_cli_app_mtd}" == "yes"]) -AM_CONDITIONAL([OPENTHREAD_ENABLE_CLI_FTD], [test "${enable_cli_app_ftd}" == "yes"]) +AM_CONDITIONAL([OPENTHREAD_ENABLE_CLI_FTD], [test "${enable_cli_app_ftd}" == "yes" -o "${OPENTHREAD_ENABLE_POSIX_APP_CLI}" == "1"]) AC_SUBST(OPENTHREAD_ENABLE_CLI) AC_SUBST(OPENTHREAD_ENABLE_CLI_FTD) AC_SUBST(OPENTHREAD_ENABLE_CLI_MTD) @@ -496,9 +549,9 @@ AC_SUBST(OPENTHREAD_ENABLE_NCP_MTD) AC_SUBST(OPENTHREAD_ENABLE_NCP_FTD) AC_SUBST(OPENTHREAD_ENABLE_NCP_RADIO) -AM_CONDITIONAL([OPENTHREAD_ENABLE_NCP], [test "${enable_ncp_app}" == "yes"]) +AM_CONDITIONAL([OPENTHREAD_ENABLE_NCP], [test "${enable_ncp_app}" == "yes" -o "${OPENTHREAD_ENABLE_POSIX_APP_NCP}" == "1"]) AM_CONDITIONAL([OPENTHREAD_ENABLE_NCP_MTD], [test "${enable_ncp_app_mtd}" == "yes"]) -AM_CONDITIONAL([OPENTHREAD_ENABLE_NCP_FTD], [test "${enable_ncp_app_ftd}" == "yes"]) +AM_CONDITIONAL([OPENTHREAD_ENABLE_NCP_FTD], [test "${enable_ncp_app_ftd}" == "yes" -o "${OPENTHREAD_ENABLE_POSIX_APP_NCP}" == "1"]) AM_CONDITIONAL([OPENTHREAD_ENABLE_NCP_RADIO], [test "${enable_ncp_app_radio}" == "yes"]) # @@ -1299,8 +1352,12 @@ AC_ARG_WITH(examples, [Specify the examples from one of: none, posix, cc2538, cc2650, cc2652, da15000, efr32, emsk, gp712, kw41z, nrf52840, samr21 @<:@default=none@:>@.])], [ case "${with_examples}" in - - none|posix|cc2538|cc2650|cc2652|da15000|efr32|emsk|gp712|kw41z|nrf52840|samr21) + none) + ;; + posix|cc2538|cc2650|cc2652|da15000|efr32|emsk|gp712|kw41z|nrf52840|samr21) + if test ${enable_posix_app} = "yes"; then + AC_MSG_ERROR([--with-examples must be none when POSIX apps are enabled by --enable-posix-app]) + fi ;; *) AC_MSG_ERROR([Invalid value ${with_examples} for --with-examples]) @@ -1446,6 +1503,8 @@ NL_ENABLE_DOCS([auto],[NO]) AM_CONDITIONAL(OPENTHREAD_BUILD_DOCS, [test "${nl_cv_build_docs}" = "yes"]) +AM_CONDITIONAL(OPENTHREAD_POSIX, [test "${OPENTHREAD_ENABLE_POSIX_APP}" = "1" -o "${OPENTHREAD_EXAMPLES_POSIX}" = "1"]) + # # Checks for libraries and packages. # @@ -1541,6 +1600,8 @@ src/cli/Makefile src/ncp/Makefile src/core/Makefile src/diag/Makefile +src/posix/Makefile +src/posix/platform/Makefile third_party/Makefile third_party/mbedtls/Makefile examples/Makefile diff --git a/src/Makefile.am b/src/Makefile.am index b4ae2bce5..d2e32e2c8 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -35,6 +35,7 @@ DIST_SUBDIRS = \ cli \ ncp \ diag \ + posix \ $(NULL) # Always build (e.g. for 'make all') these subdirectories. @@ -55,12 +56,17 @@ if OPENTHREAD_ENABLE_DIAG SUBDIRS += diag endif +if OPENTHREAD_ENABLE_POSIX_APP +SUBDIRS += posix +endif + # Always pretty (e.g. for 'make pretty') these subdirectories. PRETTY_SUBDIRS = \ cli \ core \ ncp \ + posix \ $(NULL) include $(abs_top_nlbuild_autotools_dir)/automake/post.am diff --git a/src/cli/Makefile.am b/src/cli/Makefile.am index 969f06cae..09c489ca7 100644 --- a/src/cli/Makefile.am +++ b/src/cli/Makefile.am @@ -120,6 +120,11 @@ CPPFLAGS_COMMON = \ $(OPENTHREAD_TARGET_DEFINES) \ $(NULL) +if OPENTHREAD_POSIX +CPPFLAGS_COMMON += \ + -DOPENTHREAD_POSIX=1 \ + $(NULL) +endif libopenthread_cli_mtd_a_CPPFLAGS = \ -DOPENTHREAD_MTD=1 \ diff --git a/src/cli/cli.cpp b/src/cli/cli.cpp index f4beb8a98..acd499478 100644 --- a/src/cli/cli.cpp +++ b/src/cli/cli.cpp @@ -77,7 +77,7 @@ #include "cli_coap.hpp" #endif -#if (OPENTHREAD_CONFIG_LOG_OUTPUT == OPENTHREAD_CONFIG_LOG_OUTPUT_DEBUG_UART) && OPENTHREAD_EXAMPLES_POSIX +#if (OPENTHREAD_CONFIG_LOG_OUTPUT == OPENTHREAD_CONFIG_LOG_OUTPUT_DEBUG_UART) && OPENTHREAD_POSIX #include #endif @@ -125,10 +125,10 @@ const struct Command Interpreter::sCommands[] = { {"eidcache", &Interpreter::ProcessEidCache}, #endif {"eui64", &Interpreter::ProcessEui64}, -#ifdef OPENTHREAD_EXAMPLES_POSIX +#if OPENTHREAD_POSIX {"exit", &Interpreter::ProcessExit}, #endif -#if (OPENTHREAD_CONFIG_LOG_OUTPUT == OPENTHREAD_CONFIG_LOG_OUTPUT_DEBUG_UART) && OPENTHREAD_EXAMPLES_POSIX +#if (OPENTHREAD_CONFIG_LOG_OUTPUT == OPENTHREAD_CONFIG_LOG_OUTPUT_DEBUG_UART) && OPENTHREAD_POSIX {"logfilename", &Interpreter::ProcessLogFilename}, #endif {"extaddr", &Interpreter::ProcessExtAddress}, @@ -919,7 +919,7 @@ exit: AppendResult(error); } -#ifdef OPENTHREAD_EXAMPLES_POSIX +#if OPENTHREAD_POSIX void Interpreter::ProcessExit(int argc, char *argv[]) { exit(EXIT_SUCCESS); @@ -928,7 +928,7 @@ void Interpreter::ProcessExit(int argc, char *argv[]) } #endif -#if (OPENTHREAD_CONFIG_LOG_OUTPUT == OPENTHREAD_CONFIG_LOG_OUTPUT_DEBUG_UART) && OPENTHREAD_EXAMPLES_POSIX +#if (OPENTHREAD_CONFIG_LOG_OUTPUT == OPENTHREAD_CONFIG_LOG_OUTPUT_DEBUG_UART) && OPENTHREAD_POSIX void Interpreter::ProcessLogFilename(int argc, char *argv[]) { diff --git a/src/cli/cli.hpp b/src/cli/cli.hpp index 560c607e7..12275970d 100644 --- a/src/cli/cli.hpp +++ b/src/cli/cli.hpp @@ -215,10 +215,10 @@ private: void ProcessEidCache(int argc, char *argv[]); #endif void ProcessEui64(int argc, char *argv[]); -#ifdef OPENTHREAD_EXAMPLES_POSIX +#if OPENTHREAD_POSIX void ProcessExit(int argc, char *argv[]); #endif -#if (OPENTHREAD_CONFIG_LOG_OUTPUT == OPENTHREAD_CONFIG_LOG_OUTPUT_DEBUG_UART) && OPENTHREAD_EXAMPLES_POSIX +#if (OPENTHREAD_CONFIG_LOG_OUTPUT == OPENTHREAD_CONFIG_LOG_OUTPUT_DEBUG_UART) && OPENTHREAD_POSIX void ProcessLogFilename(int argc, char *argv[]); #endif void ProcessExtAddress(int argc, char *argv[]); diff --git a/src/cli/cli_uart.cpp b/src/cli/cli_uart.cpp index 673e688b7..99b7ad648 100644 --- a/src/cli/cli_uart.cpp +++ b/src/cli/cli_uart.cpp @@ -132,7 +132,7 @@ void Uart::ReceiveTask(const uint8_t *aBuf, uint16_t aBufLength) break; -#ifdef OPENTHREAD_EXAMPLES_POSIX +#if OPENTHREAD_POSIX case 0x04: // ASCII for Ctrl-D exit(EXIT_SUCCESS); diff --git a/src/posix/Makefile-posix b/src/posix/Makefile-posix new file mode 100644 index 000000000..5589ecf80 --- /dev/null +++ b/src/posix/Makefile-posix @@ -0,0 +1,294 @@ +# +# 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. +# + +# Don't allow this top-level makefile's targets to be built in parallel. + +.NOTPARALLEL: + +COVERAGE ?= 0 +DEBUG ?= 0 + +ECHO := @echo +MAKE := make +MKDIR_P := mkdir -p +LN_S := ln -s +RM_F := rm -f + +BuildJobs ?= 10 + +# If the user has asserted COVERAGE, alter the configuration options +# accordingly. + +configure_OPTIONS = \ + --enable-application-coap \ + --enable-border-router \ + --enable-cert-log \ + --enable-child-supervision \ + --enable-commissioner \ + --enable-dhcp6-client \ + --enable-dhcp6-server \ + --enable-diag \ + --enable-dns-client \ + --enable-jam-detection \ + --enable-joiner \ + --enable-legacy \ + --enable-mac-filter \ + --enable-posix-app=all \ + --enable-service \ + --enable-tmf-proxy \ + --with-ncp-bus=uart \ + $(NULL) + +TopSourceDir := $(dir $(shell readlink $(firstword $(MAKEFILE_LIST))))../.. +AbsTopSourceDir := $(dir $(realpath $(firstword $(MAKEFILE_LIST))))../.. + +CONFIG_FILE = OPENTHREAD_PROJECT_CORE_CONFIG_FILE='\"openthread-core-posix-config.h\"' +CONFIG_FILE_PATH = $(AbsTopSourceDir)/src/posix/platform + +COMMONCFLAGS := \ + -O0 \ + -g \ + -D$(CONFIG_FILE) \ + -I$(CONFIG_FILE_PATH) \ + $(NULL) + +CPPFLAGS += \ + $(COMMONCFLAGS) \ + $(NULL) + +CFLAGS += \ + $(COMMONCFLAGS) \ + $(NULL) + +CXXFLAGS += \ + $(COMMONCFLAGS) \ + $(NULL) + +LDFLAGS += \ + $(COMMONCFLAGS) \ + $(NULL) + +INSTALL := /usr/bin/install +INSTALLFLAGS := -p + +BuildPath = build/posix +TopBuildDir = $(BuildPath) +AbsTopBuildDir = $(PWD)/$(TopBuildDir) + +ResultPath = output/posix +TopResultDir = $(ResultPath) +AbsTopResultDir = $(PWD)/$(TopResultDir) + +TargetTuple = $(shell ${AbsTopSourceDir}/third_party/nlbuild-autotools/repo/third_party/autoconf/config.guess | sed -e 's/[[:digit:].]*$$//g') + +ifndef BuildJobs +BuildJobs := $(shell getconf _NPROCESSORS_ONLN) +endif +JOBSFLAG := -j$(BuildJobs) + +# +# configure-arch +# +# Configure OpenThread for the specified target. +# +# target - The target to configure. +# +define configure-target +$(ECHO) " CONFIG $(1)..." +(cd $(BuildPath)/$(1) && $(AbsTopSourceDir)/configure \ +INSTALL="$(INSTALL) $(INSTALLFLAGS)" \ +CPPFLAGS="$(CPPFLAGS)" CFLAGS="$(CFLAGS)" CXXFLAGS="$(CXXFLAGS)" LDFLAGS="$(LDFLAGS)" \ +--prefix=/ \ +--exec-prefix=/$(1) \ +$(configure_OPTIONS)) +endef # configure-target + +# +# build-target +# +# Build the OpenThread intermediate build products for the specified +# target. +# +# target - The target to build. +# +define build-target +$(ECHO) " BUILD $(1)" +$(MAKE) $(JOBSFLAG) -C $(BuildPath)/$(1) --no-print-directory \ +all +endef # build-target + +# +# check-target +# +# Check (run unit tests) OpenThread for the specified target. +# +# target - The target to check. +# +define check-target +$(ECHO) " CHECK $(1)" +$(MAKE) $(JOBSFLAG) -C $(BuildPath)/$(1) --no-print-directory \ +check +endef # check-target + +# +# distcheck-target +# +# Check (run unit tests) OpenThread for the specified target. +# +# target - The target to distcheck. +# +define distcheck-target +$(ECHO) " DISTCHECK $(1)" +$(MAKE) $(JOBSFLAG) -C $(BuildPath)/$(1) --no-print-directory \ +distcheck +endef # distcheck-target + +# +# coverage-target +# +# Generate code coverage from unit tests for OpenThread for the +# specified target. +# +# target - The target to generate code coverage for. +# +define coverage-target +$(ECHO) " COVERAGE $(1)" +$(MAKE) $(JOBSFLAG) -C $(BuildPath)/$(1) --no-print-directory \ +coverage +endef # coverage-target + +# +# stage-target +# +# Stage (install) the OpenThread final build products for the specified +# target. +# +# target - The target to stage. +# +define stage-target +$(ECHO) " STAGE $(1)" +$(MAKE) $(JOBSFLAG) -C $(BuildPath)/$(1) --no-print-directory \ +DESTDIR=$(AbsTopResultDir) \ +install +endef # stage-target + +# +# TARGET_template +# +# Define macros, targets and rules to configure, build, and stage +# OpenThread for a single target. +# +# target - The target to instantiate the template for. +# +define TARGET_template +CONFIGURE_TARGETS += configure-$(1) +BUILD_TARGETS += do-build-$(1) +CHECK_TARGETS += check-$(1) +DISTCHECK_TARGETS += distcheck-$(1) +COVERAGE_TARGETS += coverage-$(1) +STAGE_TARGETS += stage-$(1) +BUILD_DIRS += $(BuildPath)/$(1) +DIRECTORIES += $(BuildPath)/$(1) + +configure-$(1): $(BuildPath)/$(1)/config.status + +$(BuildPath)/$(1)/config.status: | $(BuildPath)/$(1) + $$(call configure-target,$(1)) + +do-build-$(1): configure-$(1) + +do-build-$(1): + +$$(call build-target,$(1)) + +check-$(1): do-build-$(1) + +check-$(1): + +$$(call check-target,$(1)) + +distcheck-$(1): do-build-$(1) + +distcheck-$(1): + +$$(call distcheck-target,$(1)) + +coverage-$(1): do-build-$(1) + +coverage-$(1): + +$$(call coverage-target,$(1)) + +stage-$(1): do-build-$(1) + +stage-$(1): | $(TopResultDir) + $$(call stage-target,$(1)) + +$(1): stage-$(1) +endef # TARGET_template + +.DEFAULT_GOAL := all + +all: stage + +# Instantiate an target-specific build template for the target. + +$(eval $(call TARGET_template,$(TargetTuple))) + +# +# Common / Finalization +# + +configure: $(CONFIGURE_TARGETS) + +build: $(BUILD_TARGETS) + +check: $(CHECK_TARGETS) + +distcheck: $(DISTCHECK_TARGETS) + +coverage: $(COVERAGE_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) "target:" + $(ECHO) "" + $(ECHO) " $(TargetTuple)" + $(ECHO) "" diff --git a/src/posix/Makefile.am b/src/posix/Makefile.am new file mode 100644 index 000000000..9c839980b --- /dev/null +++ b/src/posix/Makefile.am @@ -0,0 +1,150 @@ +# +# Copyright (c) 2018, 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 + +# Always package (e.g. for 'make dist') these subdirectories. + +DIST_SUBDIRS = \ + platform \ + $(NULL) + +# Always build (e.g. for 'make all') these subdirectories. + +SUBDIRS = \ + platform \ + $(NULL) + +# Always pretty (e.g. for 'make pretty') these subdirectories. + +PRETTY_SUBDIRS = \ + platform \ + $(NULL) + +CPPFLAGS_COMMON = \ + -I$(top_srcdir)/include \ + -I$(top_srcdir)/src/core \ + -I$(top_srcdir)/src/posix/platform \ + -D_GNU_SOURCE \ + $(NULL) + +LIBTOOLFLAGS_COMMON = --preserve-dup-deps + +LDADD_COMMON = \ + $(top_builddir)/src/posix/platform/libopenthread-posix.a \ + -lstdc++ \ + -lutil \ + $(NULL) + +if OPENTHREAD_ENABLE_BUILTIN_MBEDTLS +LDADD_COMMON += \ + $(top_builddir)/third_party/mbedtls/libmbedcrypto.a \ + $(NULL) +endif # OPENTHREAD_ENABLE_BUILTIN_MBEDTLS + +if OPENTHREAD_ENABLE_DIAG +LDADD_COMMON += \ + $(top_builddir)/src/diag/libopenthread-diag.a \ + $(NULL) +endif # OPENTHREAD_ENABLE_DIAG + +bin_PROGRAMS = \ + $(NULL) + +if OPENTHREAD_ENABLE_POSIX_APP_NCP +bin_PROGRAMS += \ + ot-ncp \ + $(NULL) +endif + +ot_ncp_CPPFLAGS = \ + $(CPPFLAGS_COMMON) \ + -DOPENTHREAD_POSIX_APP=1 \ + $(NULL) + +ot_ncp_SOURCES = \ + main.c \ + $(NULL) + +ot_ncp_LDADD = \ + $(top_builddir)/src/ncp/libopenthread-ncp-ftd.a \ + $(top_builddir)/src/core/libopenthread-ftd.a \ + $(LDADD_COMMON) \ + $(top_builddir)/src/core/libopenthread-ftd.a \ + $(LDADD_COMMON) \ + $(NULL) + +ot_ncp_LDFLAGS = \ + $(LDFLAGS_COMMON) \ + $(NULL) + +ot_ncp_LIBTOOLFLAGS = \ + $(LIBTOOLFLAGS_COMMON) \ + $(NULL) + +if OPENTHREAD_ENABLE_POSIX_APP_CLI +bin_PROGRAMS += \ + ot-cli \ + $(NULL) +endif + +ot_cli_CPPFLAGS = \ + $(CPPFLAGS_COMMON) \ + -DOPENTHREAD_POSIX_APP=2 \ + $(NULL) + +ot_cli_SOURCES = \ + main.c \ + $(NULL) + +ot_cli_LDADD = \ + $(top_builddir)/src/cli/libopenthread-cli-ftd.a \ + $(top_builddir)/src/core/libopenthread-ftd.a \ + $(LDADD_COMMON) \ + $(top_builddir)/src/core/libopenthread-ftd.a \ + $(LDADD_COMMON) \ + $(NULL) + +ot_cli_LDFLAGS = \ + $(LDFLAGS_COMMON) \ + $(NULL) + +ot_cli_LIBTOOLFLAGS = \ + $(LIBTOOLFLAGS_COMMON) \ + $(NULL) + +if OPENTHREAD_ENABLE_LINKER_MAP +ot_ncp_LDFLAGS += -Wl,-Map=ot-cli-ftd.map +ot_cli_LDFLAGS += -Wl,-Map=ot-cli-mtd.map +endif + +if OPENTHREAD_BUILD_COVERAGE +CLEANFILES = $(wildcard *.gcda *.gcno) +endif # OPENTHREAD_BUILD_COVERAGE + +include $(abs_top_nlbuild_autotools_dir)/automake/post.am diff --git a/src/posix/main.c b/src/posix/main.c new file mode 100644 index 000000000..c3da2c81a --- /dev/null +++ b/src/posix/main.c @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2018, 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 + +#define OPENTHREAD_POSIX_APP_NCP 1 +#define OPENTHREAD_POSIX_APP_CLI 2 + +#include +#if OPENTHREAD_POSIX_APP == OPENTHREAD_POSIX_APP_NCP +#include +#elif OPENTHREAD_POSIX_APP == OPENTHREAD_POSIX_APP_CLI +#include +#else +#error "Unknown posix app mode!" +#endif +#include +#include + +#include "platform.h" + +void otTaskletsSignalPending(otInstance *aInstance) +{ + (void)aInstance; +} + +int main(int argc, char *argv[]) +{ + otInstance *sInstance; + +pseudo_reset: + + PlatformInit(argc, argv); + + sInstance = otInstanceInitSingle(); + assert(sInstance); + +#if OPENTHREAD_POSIX_APP == OPENTHREAD_POSIX_APP_NCP + otNcpInit(sInstance); +#elif OPENTHREAD_POSIX_APP == OPENTHREAD_POSIX_APP_CLI + otCliUartInit(sInstance); +#endif + +#if OPENTHREAD_ENABLE_DIAG + otDiagInit(sInstance); +#endif + + while (!PlatformPseudoResetWasRequested()) + { + otTaskletsProcess(sInstance); + PlatformProcessDrivers(sInstance); + } + + otInstanceFinalize(sInstance); + + goto pseudo_reset; + + return 0; +} + +/* + * Provide, if required an "otPlatLog()" function + */ +#if OPENTHREAD_CONFIG_LOG_OUTPUT == OPENTHREAD_CONFIG_LOG_OUTPUT_APP +void otPlatLog(otLogLevel aLogLevel, otLogRegion aLogRegion, const char *aFormat, ...) +{ + OT_UNUSED_VARIABLE(aLogLevel); + OT_UNUSED_VARIABLE(aLogRegion); + OT_UNUSED_VARIABLE(aFormat); + + va_list ap; + va_start(ap, aFormat); +#if OPENTHREAD_POSIX_APP == OPENTHREAD_POSIX_APP_NCP + otCliPlatLogv(aLogLevel, aLogRegion, aFormat, ap); +#elif OPENTHREAD_POSIX_APP == OPENTHREAD_POSIX_APP_CLI + otNcpPlatLogv(aLogLevel, aLogRegion, aFormat, ap); +#endif + va_end(ap); +} +#endif diff --git a/src/posix/platform/Makefile.am b/src/posix/platform/Makefile.am new file mode 100644 index 000000000..bbe87b2da --- /dev/null +++ b/src/posix/platform/Makefile.am @@ -0,0 +1,75 @@ +# +# Copyright (c) 2016-2018, 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 + +lib_LIBRARIES = libopenthread-posix.a + +libopenthread_posix_a_CPPFLAGS = \ + -I$(top_srcdir)/include \ + -I$(top_srcdir)/src/core \ + -I$(top_srcdir)/src/ncp \ + -I$(top_srcdir)/src/posix/platform \ + -D_GNU_SOURCE \ + $(NULL) + +libopenthread_posix_a_SOURCES = \ + alarm.c \ + diag.c \ + flash.c \ + frame_queue.cpp \ + hdlc.cpp \ + logging.c \ + misc.c \ + platform.c \ + radio_spinel.cpp \ + random.c \ + settings.cpp \ + spi-stubs.c \ + spinel.c \ + uart.c \ + $(NULL) + +noinst_HEADERS = \ + flash.h \ + frame_queue.hpp \ + platform.h \ + platform-posix.h \ + radio_spinel.hpp \ + $(NULL) + +PRETTY_FILES = \ + $(libopenthread_posix_a_SOURCES) \ + $(noinst_HEADERS) \ + $(NULL) + +if OPENTHREAD_BUILD_COVERAGE +CLEANFILES = $(wildcard *.gcda *.gcno) +endif # OPENTHREAD_BUILD_COVERAGE + +include $(abs_top_nlbuild_autotools_dir)/automake/post.am diff --git a/src/posix/platform/README.md b/src/posix/platform/README.md new file mode 100644 index 000000000..6b5f83aac --- /dev/null +++ b/src/posix/platform/README.md @@ -0,0 +1,59 @@ +# OpenThread on POSIX + +This directory contains platform drivers for POSIX with an external radio tranceiver. + +## Build + +```bash +$ cd +$ ./bootstrap +$ make -f src/posix/Makefile-posix +``` + +After a successful build, the `elf` files are found in +`/output/posix//bin`. + +## + +## Interact + +1. Spawn the process: + +`stty` is required. + +```bash +$ cd /output//bin +$ ./ot-cli /dev/ttyUSB0 115200 +``` + +2. Type `help` for list of commands. + +```bash +> help +help +channel +childtimeout +contextreusedelay +extaddr +extpanid +ipaddr +keysequence +leaderweight +masterkey +mode +netdataregister +networkidtimeout +networkname +panid +ping +prefix +releaserouterid +rloc16 +route +routerupgradethreshold +scan +start +state +stop +whitelist +``` diff --git a/src/posix/platform/alarm.c b/src/posix/platform/alarm.c new file mode 100644 index 000000000..b0dcc5545 --- /dev/null +++ b/src/posix/platform/alarm.c @@ -0,0 +1,195 @@ +/* + * 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 "platform-posix.h" + +#include +#include +#include + +#include +#include +#include + +#define MS_PER_S 1000 +#define US_PER_MS 1000 +#define US_PER_S 1000000 + +#define DEFAULT_TIMEOUT 10 // seconds + +static bool sIsMsRunning = false; +static uint32_t sMsAlarm = 0; + +static bool sIsUsRunning = false; +static uint32_t sUsAlarm = 0; + +static uint32_t sSpeedUpFactor = 1; + +static struct timeval sStart; + +void platformAlarmInit(uint32_t aSpeedUpFactor) +{ + sSpeedUpFactor = aSpeedUpFactor; + gettimeofday(&sStart, NULL); +} + +uint32_t otPlatAlarmMilliGetNow(void) +{ + struct timeval tv; + + gettimeofday(&tv, NULL); + timersub(&tv, &sStart, &tv); + + return (uint32_t)(((uint64_t)tv.tv_sec * sSpeedUpFactor * MS_PER_S) + + ((uint64_t)tv.tv_usec * sSpeedUpFactor / US_PER_MS)); +} + +void otPlatAlarmMilliStartAt(otInstance *aInstance, uint32_t aT0, uint32_t aDt) +{ + (void)aInstance; + sMsAlarm = aT0 + aDt; + sIsMsRunning = true; +} + +void otPlatAlarmMilliStop(otInstance *aInstance) +{ + (void)aInstance; + sIsMsRunning = false; +} + +uint32_t otPlatAlarmMicroGetNow(void) +{ + struct timeval tv; + + gettimeofday(&tv, NULL); + timersub(&tv, &sStart, &tv); + + return (uint32_t)(tv.tv_sec * US_PER_S + tv.tv_usec) * sSpeedUpFactor; +} + +void otPlatAlarmMicroStartAt(otInstance *aInstance, uint32_t aT0, uint32_t aDt) +{ + (void)aInstance; + sUsAlarm = aT0 + aDt; + sIsUsRunning = true; +} + +void otPlatAlarmMicroStop(otInstance *aInstance) +{ + (void)aInstance; + sIsUsRunning = false; +} + +void platformAlarmUpdateTimeout(struct timeval *aTimeout) +{ + int32_t usRemaining = DEFAULT_TIMEOUT * US_PER_S; + int32_t msRemaining = DEFAULT_TIMEOUT * MS_PER_S; + + if (aTimeout == NULL) + { + return; + } + + if (sIsUsRunning) + { + usRemaining = (int32_t)(sUsAlarm - otPlatAlarmMicroGetNow()); + } + + if (sIsMsRunning) + { + msRemaining = (int32_t)(sMsAlarm - otPlatAlarmMilliGetNow()); + } + + if (usRemaining <= 0 || msRemaining <= 0) + { + aTimeout->tv_sec = 0; + aTimeout->tv_usec = 0; + } + else + { + int64_t remaining = ((int64_t)msRemaining) * US_PER_MS; + + if (usRemaining < remaining) + { + remaining = usRemaining; + } + + remaining /= sSpeedUpFactor; + + if (remaining == 0) + { + remaining = 1; + } + + aTimeout->tv_sec = (time_t)remaining / US_PER_S; + aTimeout->tv_usec = remaining % US_PER_S; + } +} + +void platformAlarmProcess(otInstance *aInstance) +{ + int32_t remaining; + + if (sIsMsRunning) + { + remaining = (int32_t)(sMsAlarm - otPlatAlarmMilliGetNow()); + + if (remaining <= 0) + { + sIsMsRunning = false; + +#if OPENTHREAD_ENABLE_DIAG + + if (otPlatDiagModeGet()) + { + otPlatDiagAlarmFired(aInstance); + } + else +#endif + { + otPlatAlarmMilliFired(aInstance); + } + } + } + +#if OPENTHREAD_CONFIG_ENABLE_PLATFORM_USEC_TIMER + + if (sIsUsRunning) + { + remaining = (int32_t)(sUsAlarm - otPlatAlarmMicroGetNow()); + + if (remaining <= 0) + { + sIsUsRunning = false; + + otPlatAlarmMicroFired(aInstance); + } + } + +#endif // OPENTHREAD_CONFIG_ENABLE_PLATFORM_USEC_TIMER +} diff --git a/src/posix/platform/code_utils.h b/src/posix/platform/code_utils.h new file mode 100644 index 000000000..46b0d0012 --- /dev/null +++ b/src/posix/platform/code_utils.h @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2017, 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 macros for validating runtime conditions. + */ + +#ifndef CODE_UTILS_H +#define CODE_UTILS_H + +/** + * This checks for the specified condition, which is expected to + * commonly be true, and branches to the local label 'exit' if the + * condition is false. + * + * @param[in] aCondition A Boolean expression to be evaluated. + * + */ +#define otEXPECT(aCondition) \ + do \ + { \ + if (!(aCondition)) \ + { \ + goto exit; \ + } \ + } while (0) + +/** + * This checks for the specified condition, which is expected to + * commonly be true, and both executes @p anAction and branches to + * the local label 'exit' if the condition is false. + * + * @param[in] aCondition A Boolean expression to be evaluated. + * @param[in] aAction An expression or block to execute when the + * assertion fails. + * + */ +#define otEXPECT_ACTION(aCondition, aAction) \ + do \ + { \ + if (!(aCondition)) \ + { \ + aAction; \ + goto exit; \ + } \ + } while (0) + +/** + * This unconditionally executes @aAction and branches to the local + * label 'exit'. + * + * @note The use of this interface implies neither success nor + * failure for the overall exit status of the enclosing + * function body. + * + * @param[in] aAction An expression or block to execute. + * + */ +#define otEXIT_NOW(aAction) \ + do \ + { \ + aAction; \ + goto exit; \ + } while (0) + +#endif // CODE_UTILS_H diff --git a/src/posix/platform/debug_uart.c b/src/posix/platform/debug_uart.c new file mode 100644 index 000000000..40d0ea214 --- /dev/null +++ b/src/posix/platform/debug_uart.c @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2017, 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 + +/* + * Implementation note: + * These are all "weak" so that a platform may if it chooses override the instance. + */ + +OT_TOOL_WEAK +void otPlatDebugUart_printf(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + otPlatDebugUart_vprintf(fmt, ap); + va_end(ap); +} + +OT_TOOL_WEAK +void otPlatDebugUart_vprintf(const char *fmt, va_list ap) +{ + char buf[128]; + /* by standard ... + * vsnprintf() always null terminates + */ + vsnprintf(buf, sizeof(buf), fmt, ap); + /* however ... some platforms have bugs */ + buf[sizeof(buf) - 1] = 0; + otPlatDebugUart_puts_no_nl(buf); +} + +OT_TOOL_WEAK +void otPlatDebugUart_write_bytes(const uint8_t *pBytes, int nBytes) +{ + while (nBytes > 0) + { + otPlatDebugUart_putchar((int)(*pBytes)); + pBytes++; + nBytes--; + } +} + +OT_TOOL_WEAK +void otPlatDebugUart_puts_no_nl(const char *s) +{ + while (*s) + { + otPlatDebugUart_putchar(*s); + s++; + } +} + +OT_TOOL_WEAK +void otPlatDebugUart_puts(const char *s) +{ + otPlatDebugUart_puts_no_nl(s); + otPlatDebugUart_putchar('\n'); +} + +OT_TOOL_WEAK +void otPlatDebugUart_putchar(int c) +{ + /* map lf to crlf as needed */ + if (c == '\n') + { + otPlatDebugUart_putchar_raw('\r'); + } + + otPlatDebugUart_putchar_raw(c); +} + +/* provide WEAK stubs for platforms that do not implement all functions */ +OT_TOOL_WEAK +void otPlatDebugUart_putchar_raw(int c) +{ + OT_UNUSED_VARIABLE(c); +} + +OT_TOOL_WEAK +int otPlatDebugUart_kbhit(void) +{ + return 0; /* nothing */ +} + +OT_TOOL_WEAK +int otPlatDebugUart_getc(void) +{ + return -1; /* nothing */ +} + +OT_TOOL_WEAK +otError otPlatDebugUart_logfile(const char *filename) +{ + OT_UNUSED_VARIABLE(filename); + return OT_ERROR_FAILED; +} + +#if (OPENTHREAD_CONFIG_LOG_OUTPUT == OPENTHREAD_CONFIG_LOG_OUTPUT_DEBUG_UART) +/* this should not be a WEAK function */ +void otPlatLog(otLogLevel aLogLevel, otLogRegion aLogRegion, const char *aFormat, ...) +{ + va_list ap; + uint32_t now; + + OT_UNUSED_VARIABLE(aLogLevel); + OT_UNUSED_VARIABLE(aLogRegion); + + now = otPlatAlarmMilliGetNow(); + otPlatDebugUart_printf("%3d.%03d | ", (int)(now / 1000), (int)(now % 1000)); + va_start(ap, aFormat); + otPlatDebugUart_vprintf(aFormat, ap); + va_end(ap); + + otPlatDebugUart_putchar('\n'); +} +#endif diff --git a/src/posix/platform/diag.c b/src/posix/platform/diag.c new file mode 100644 index 000000000..7a49dd91a --- /dev/null +++ b/src/posix/platform/diag.c @@ -0,0 +1,89 @@ +/* + * 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 "platform-posix.h" + +#include +#include +#include +#include + +#include +#include +#include +#include + +#if OPENTHREAD_ENABLE_DIAG + +/** + * Diagnostics mode variables. + * + */ +static bool sDiagMode = false; + +void otPlatDiagProcess(otInstance *aInstance, int argc, char *argv[], char *aOutput, size_t aOutputMaxLen) +{ + // Add more plarform specific diagnostics features here. + snprintf(aOutput, aOutputMaxLen, "diag feature '%s' is not supported\r\n", argv[0]); + (void)argc; + (void)aInstance; +} + +void otPlatDiagModeSet(bool aMode) +{ + sDiagMode = aMode; +} + +bool otPlatDiagModeGet() +{ + return sDiagMode; +} + +void otPlatDiagChannelSet(uint8_t aChannel) +{ + (void)aChannel; +} + +void otPlatDiagTxPowerSet(int8_t aTxPower) +{ + (void)aTxPower; +} + +void otPlatDiagRadioReceived(otInstance *aInstance, otRadioFrame *aFrame, otError aError) +{ + (void)aInstance; + (void)aFrame; + (void)aError; +} + +void otPlatDiagAlarmCallback(otInstance *aInstance) +{ + (void)aInstance; +} + +#endif // OPENTHREAD_ENABLE_DIAG diff --git a/src/posix/platform/flash.c b/src/posix/platform/flash.c new file mode 100644 index 000000000..31837739d --- /dev/null +++ b/src/posix/platform/flash.c @@ -0,0 +1,163 @@ +/* + * 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 "platform-posix.h" + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "code_utils.h" +#include "flash.h" + +static int sFlashFd = -1; +uint32_t sEraseAddress; + +enum +{ + FLASH_SIZE = 0x40000, + FLASH_PAGE_SIZE = 0x800, + FLASH_PAGE_NUM = 128, +}; + +otError utilsFlashInit(void) +{ + otError error = OT_ERROR_NONE; + char fileName[32]; + struct stat st; + bool create = false; + const char *offset = getenv("PORT_OFFSET"); + + memset(&st, 0, sizeof(st)); + + if (stat("tmp", &st) == -1) + { + mkdir("tmp", 0777); + } + + if (offset == NULL) + { + offset = "0"; + } + + snprintf(fileName, sizeof(fileName), "tmp/%s_%" PRIx64 ".flash", offset, NODE_ID); + + if (access(fileName, 0)) + { + create = true; + } + + sFlashFd = open(fileName, O_RDWR | O_CREAT, 0666); + lseek(sFlashFd, 0, SEEK_SET); + + otEXPECT_ACTION(sFlashFd >= 0, error = OT_ERROR_FAILED); + + if (create) + { + for (uint16_t index = 0; index < FLASH_PAGE_NUM; index++) + { + error = utilsFlashErasePage(index * FLASH_PAGE_SIZE); + otEXPECT(error == OT_ERROR_NONE); + } + } + +exit: + return error; +} + +uint32_t utilsFlashGetSize(void) +{ + return FLASH_SIZE; +} + +otError utilsFlashErasePage(uint32_t aAddress) +{ + otError error = OT_ERROR_NONE; + uint32_t address; + uint8_t dummyPage[FLASH_SIZE]; + + otEXPECT_ACTION(sFlashFd >= 0, error = OT_ERROR_FAILED); + otEXPECT_ACTION(aAddress < FLASH_SIZE, error = OT_ERROR_INVALID_ARGS); + + // Get start address of the flash page that includes aAddress + address = aAddress & (~(uint32_t)(FLASH_PAGE_SIZE - 1)); + + // set the page to the erased state. + memset((void *)(&dummyPage[0]), 0xff, FLASH_PAGE_SIZE); + + // Write the page + ssize_t r; + r = pwrite(sFlashFd, &(dummyPage[0]), FLASH_PAGE_SIZE, (off_t)address); + otEXPECT_ACTION(((int)r) == ((int)(FLASH_PAGE_SIZE)), error = OT_ERROR_FAILED); + +exit: + return error; +} + +otError utilsFlashStatusWait(uint32_t aTimeout) +{ + (void)aTimeout; + return OT_ERROR_NONE; +} + +uint32_t utilsFlashWrite(uint32_t aAddress, uint8_t *aData, uint32_t aSize) +{ + uint32_t ret = 0; + uint32_t index = 0; + uint8_t byte; + + otEXPECT(sFlashFd >= 0 && aAddress < FLASH_SIZE); + + for (index = 0; index < aSize; index++) + { + otEXPECT((ret = utilsFlashRead(aAddress + index, &byte, 1)) == 1); + // Use bitwise AND to emulate the behavior of flash memory + byte &= aData[index]; + otEXPECT((ret = (uint32_t)pwrite(sFlashFd, &byte, 1, (off_t)(aAddress + index))) == 1); + } + +exit: + return index; +} + +uint32_t utilsFlashRead(uint32_t aAddress, uint8_t *aData, uint32_t aSize) +{ + uint32_t ret = 0; + + otEXPECT(sFlashFd >= 0 && aAddress < FLASH_SIZE); + ret = (uint32_t)pread(sFlashFd, aData, aSize, (off_t)aAddress); + +exit: + return ret; +} diff --git a/src/posix/platform/flash.h b/src/posix/platform/flash.h new file mode 100644 index 000000000..8ffc29e49 --- /dev/null +++ b/src/posix/platform/flash.h @@ -0,0 +1,129 @@ +/* + * 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 defines the flash interface used by settings.cpp. + */ + +#ifndef UTILS_FLASH_H +#define UTILS_FLASH_H + +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Perform any initialization for flash driver. + * + * @retval ::OT_ERROR_NONE Initialize flash driver success. + * @retval ::OT_ERROR_FAILED Initialize flash driver fail. + */ +otError utilsFlashInit(void); + +/** + * Get the size of flash that can be read/write by the caller. + * The usable flash size is always the multiple of flash page size. + * + * @returns The size of the flash. + */ +uint32_t utilsFlashGetSize(void); + +/** + * Erase one flash page that include the input address. + * This is a non-blocking function. It can work with utilsFlashStatusWait to check when erase is done. + * + * The flash address starts from 0, and this function maps the input address to the physical address of flash for + * erasing. 0 is always mapped to the beginning of one flash page. The input address should never be mapped to the + * firmware space or any other protected flash space. + * + * @param[in] aAddress The start address of the flash to erase. + * + * @retval OT_ERROR_NONE Erase flash operation is started. + * @retval OT_ERROR_FAILED Erase flash operation is not started. + * @retval OT_ERROR_INVALID_ARGS aAddress is out of range of flash or not aligend. + */ +otError utilsFlashErasePage(uint32_t aAddress); + +/** + * Check whether flash is ready or busy. + * + * @param[in] aTimeout The interval in milliseconds waiting for the flash operation to be done and become ready again. + * zero indicates that it is a polling function, and returns current status of flash immediately. + * non-zero indicates that it is blocking there until the operation is done and become ready, or + * timeout expires. + * + * @retval OT_ERROR_NONE Flash is ready for any operation. + * @retval OT_ERROR_BUSY Flash is busy. + */ +otError utilsFlashStatusWait(uint32_t aTimeout); + +/** + * Write flash. The write operation only clears bits, but never set bits. + * + * The flash address starts from 0, and this function maps the input address to the physical address of flash for + * writing. 0 is always mapped to the beginning of one flash page. The input address should never be mapped to the + * firmware space or any other protected flash space. + * + * @param[in] aAddress The start address of the flash to write. + * @param[in] aData The pointer of the data to write. + * @param[in] aSize The size of the data to write. + * + * @returns The actual size of octets write to flash. + * It is expected the same as aSize, and may be less than aSize. + * 0 indicates that something wrong happens when writing. + */ +uint32_t utilsFlashWrite(uint32_t aAddress, uint8_t *aData, uint32_t aSize); + +/** + * Read flash. + * + * The flash address starts from 0, and this function maps the input address to the physical address of flash for + * reading. 0 is always mapped to the beginning of one flash page. The input address should never be mapped to the + * firmware space or any other protected flash space. + * + * @param[in] aAddress The start address of the flash to read. + * @param[Out] aData The pointer of buffer for reading. + * @param[in] aSize The size of the data to read. + * + * @returns The actual size of octets read to buffer. + * It is expected the same as aSize, and may be less than aSize. + * 0 indicates that something wrong happens when reading. + */ +uint32_t utilsFlashRead(uint32_t aAddress, uint8_t *aData, uint32_t aSize); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // UTILS_FLASH_H diff --git a/src/posix/platform/frame_queue.cpp b/src/posix/platform/frame_queue.cpp new file mode 100644 index 000000000..5231e9f38 --- /dev/null +++ b/src/posix/platform/frame_queue.cpp @@ -0,0 +1,239 @@ +/* + * Copyright (c) 2018, 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 implementation of frame queue. + */ + +#include "platform-posix.h" + +#include "frame_queue.hpp" + +#include +#include + +#include "code_utils.h" + +namespace ot { + +otError FrameQueue::Push(const uint8_t *aFrame, uint8_t aLength) +{ + otError error = OT_ERROR_NONE; + uint16_t newTail = mTail + aLength + 1; + + assert(aFrame != NULL); + otEXPECT_ACTION(aFrame != NULL, error = OT_ERROR_INVALID_ARGS); + + if (mHead > mTail) + { + otEXPECT_ACTION(newTail < mHead, error = OT_ERROR_NO_BUFS); + } + else if (newTail >= sizeof(mBuffer)) + { + newTail -= sizeof(mBuffer); + otEXPECT_ACTION(newTail < mHead, error = OT_ERROR_NO_BUFS); + } + + mBuffer[mTail] = aLength; + + if (newTail > mTail) + { + memcpy(mBuffer + mTail + 1, aFrame, aLength); + } + else + { + uint16_t half = (sizeof(mBuffer) - mTail - 1); + memcpy(mBuffer + mTail + 1, aFrame, half); + memcpy(mBuffer, aFrame + half, (aLength - half)); + } + + mTail = newTail; + +exit: + return error; +} + +void FrameQueue::Shift(void) +{ + if (mHead != mTail) + { + mHead += 1 + mBuffer[mHead]; + mHead %= sizeof(mBuffer); + } +} + +const uint8_t *FrameQueue::Peek(uint8_t *aFrame, uint8_t &aLength) +{ + const uint8_t *frame = NULL; + uint16_t next; + + otEXPECT(mHead != mTail); + + aLength = mBuffer[mHead]; + next = mHead + 1 + aLength; + + if (next >= sizeof(mBuffer)) + { + uint16_t half = sizeof(mBuffer) - mHead - 1; + memcpy(aFrame, mBuffer + mHead + 1, half); + memcpy(aFrame + half, mBuffer, aLength - half); + frame = aFrame; + } + else + { + frame = mBuffer + mHead + 1; + } + +exit: + return frame; +} + +} // namespace ot + +#if SELF_TEST +#include +void TestSingle() +{ + otError error; + ot::FrameQueue frameQueue; + uint8_t length; + uint8_t frame[255]; + + for (size_t i = 0; i < sizeof(frame); ++i) + { + frame[i] = i; + } + + for (size_t i = 0; i < sizeof(frame); ++i) + { + uint8_t outFrame[255]; + const uint8_t *retFrame = NULL; + error = frameQueue.Push(frame, i); + assert(OT_ERROR_NONE == error); + assert(!frameQueue.IsEmpty()); + retFrame = frameQueue.Peek(outFrame, length); + assert(retFrame != NULL); + assert(length == i); + + for (size_t j = 0; j < i; ++j) + { + assert(retFrame[j] == frame[j]); + } + + frameQueue.Shift(); + assert(frameQueue.IsEmpty()); + } +} + +void TestMultiple() +{ + otError error; + ot::FrameQueue frameQueue; + uint8_t length; + uint8_t frame[255]; + + for (size_t i = 0; i < sizeof(frame); ++i) + { + frame[i] = i; + } + + srand(0); + + for (size_t i = 0; i < sizeof(frame); ++i) + { + uint8_t outFrame[255]; + const uint8_t *retFrame = NULL; + + int action = rand(); + if (action & 0x01) // push when odd + { + error = frameQueue.Push(frame, i); + if (error == OT_ERROR_NO_BUFS) + continue; + + assert(OT_ERROR_NONE == error); + assert(!frameQueue.IsEmpty()); + retFrame = frameQueue.Peek(outFrame, length); + assert(retFrame != NULL); + + for (size_t j = 0; j < length; ++j) + { + assert(retFrame[j] == frame[j]); + } + } + else + { + frameQueue.Shift(); + } + } +} + +void TestRing() +{ + ot::FrameQueue frameQueue; + uint8_t length; + uint8_t frame[255]; + + for (size_t i = 0; i < sizeof(frame); ++i) + { + frame[i] = i; + } + + for (size_t i = 0; i < OPENTHREAD_CONFIG_FRAME_CACHE_SIZE + 255; i += sizeof(frame)) + { + uint8_t outFrame[255]; + const uint8_t *retFrame = NULL; + + frameQueue.Push(frame, sizeof(frame)); + + retFrame = frameQueue.Peek(outFrame, length); + + for (size_t j = 0; j < sizeof(frame); ++j) + { + assert(retFrame[j] == frame[j]); + } + + frameQueue.Shift(); + }; +} + +void RunAllTests() +{ + TestSingle(); + TestMultiple(); + TestRing(); +} + +int main(void) +{ + RunAllTests(); + printf("All tests passed\n"); + return 0; +} +#endif diff --git a/src/posix/platform/frame_queue.hpp b/src/posix/platform/frame_queue.hpp new file mode 100644 index 000000000..976437b7d --- /dev/null +++ b/src/posix/platform/frame_queue.hpp @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2018, 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 frame queue. + */ + +#ifndef OT_FRAME_QUEUE_HPP_ +#define OT_FRAME_QUEUE_HPP_ + +#include + +/** + * @def OPENTHREAD_CONFIG_FRAME_QUEUE_SIZE + * + * The size of a frame queue in bytes. + * + */ +#ifndef OPENTHREAD_CONFIG_FRAME_QUEUE_SIZE +#define OPENTHREAD_CONFIG_FRAME_QUEUE_SIZE 4096 +#endif + +namespace ot { + +class FrameQueue +{ +public: + /** + * This constructor initializes a frame queue based on ring buffer. + * + */ + FrameQueue(void) + : mHead(0) + , mTail(0) + { + } + + /** + * This method checks if the cache is empty. + * + * @retval true No frames are queued. + * @retval false At least one frame is queued. + * + */ + bool IsEmpty(void) const { return mHead == mTail; } + + /** + * This method removes one frame from the head. + * + */ + void Shift(void); + + /** + * This method pushes one frame into the queue. + * + * @param[in] aFrame A pointer to a spinel frame to be queued. + * @param[in] aLength Frame length in bytes. + * + * @retval OT_ERROR_NONE Successfully queued this frame. + * @retval OT_ERROR_NO_BUFS Insufficient memory for this frame. + * + */ + otError Push(const uint8_t *aFrame, uint8_t aLength); + + /** + * This method gets one frame at head. + * + * @note aFrame is only used when necessary, always use the returned pointer to access frame data. + * + * @param[out] aFrame A pointer to the frame to receive the data. + * @param[out] aLength A reference to receive the frame length. + * + * @return A pointer to the frame. + * + */ + const uint8_t *Peek(uint8_t *aFrame, uint8_t &aLength); + +private: + enum + { + kQueueSize = OPENTHREAD_CONFIG_FRAME_QUEUE_SIZE, + }; + + uint8_t mBuffer[kQueueSize]; + uint16_t mHead; + uint16_t mTail; +}; + +} // namespace ot + +#endif // OT_FRAME_CACHE_HPP_ diff --git a/src/posix/platform/hdlc.cpp b/src/posix/platform/hdlc.cpp new file mode 120000 index 000000000..992e41183 --- /dev/null +++ b/src/posix/platform/hdlc.cpp @@ -0,0 +1 @@ +../../ncp/hdlc.cpp \ No newline at end of file diff --git a/src/posix/platform/logging.c b/src/posix/platform/logging.c new file mode 100644 index 000000000..789a8509c --- /dev/null +++ b/src/posix/platform/logging.c @@ -0,0 +1,83 @@ +/* + * 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 "platform-posix.h" +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "code_utils.h" + +// Macro to append content to end of the log string. + +#define LOG_PRINTF(...) \ + charsWritten = snprintf(&logString[offset], sizeof(logString) - offset, __VA_ARGS__); \ + otEXPECT_ACTION(charsWritten >= 0, logString[offset] = 0); \ + offset += (unsigned int)charsWritten; \ + otEXPECT_ACTION(offset < sizeof(logString), logString[sizeof(logString) - 1] = 0) + +#if (OPENTHREAD_CONFIG_LOG_OUTPUT == OPENTHREAD_CONFIG_LOG_OUTPUT_PLATFORM_DEFINED) || \ + (OPENTHREAD_CONFIG_LOG_OUTPUT == OPENTHREAD_CONFIG_LOG_OUTPUT_NCP_SPINEL) +OT_TOOL_WEAK void otPlatLog(otLogLevel aLogLevel, otLogRegion aLogRegion, const char *aFormat, ...) +{ + char logString[512]; + unsigned int offset; + int charsWritten; + va_list args; + + offset = 0; + + LOG_PRINTF("[%" PRIx64 "] ", NODE_ID); + + va_start(args, aFormat); + charsWritten = vsnprintf(&logString[offset], sizeof(logString) - offset, aFormat, args); + va_end(args); + + otEXPECT_ACTION(charsWritten >= 0, logString[offset] = 0); + +exit: +#ifndef _WIN32 + syslog(LOG_CRIT, "%s", logString); +#else + printf("%s\r\n", logString); +#endif + + (void)aLogLevel; + (void)aLogRegion; +} + +#endif // #if (OPENTHREAD_CONFIG_LOG_OUTPUT == OPENTHREAD_CONFIG_LOG_OUTPUT_PLATFORM_DEFINED) diff --git a/src/posix/platform/misc.c b/src/posix/platform/misc.c new file mode 100644 index 000000000..52d63d6f2 --- /dev/null +++ b/src/posix/platform/misc.c @@ -0,0 +1,112 @@ +/* + * 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 "platform-posix.h" + +#include + +#include +#include + +#include "platform.h" + +extern int gArgumentsCount; +extern char **gArguments; + +static otPlatResetReason sPlatResetReason = OT_PLAT_RESET_REASON_POWER_ON; +bool gPlatformPseudoResetWasRequested; +static otPlatMcuPowerState gPlatMcuPowerState = OT_PLAT_MCU_POWER_STATE_ON; + +void otPlatReset(otInstance *aInstance) +{ +#if OPENTHREAD_PLATFORM_USE_PSEUDO_RESET + gPlatformPseudoResetWasRequested = true; + sPlatResetReason = OT_PLAT_RESET_REASON_SOFTWARE; + +#else // elif OPENTHREAD_PLATFORM_USE_PSEUDO_RESET + // Restart the process using execvp. + char *argv[gArgumentsCount + 1]; + + for (int i = 0; i < gArgumentsCount; ++i) + { + argv[i] = gArguments[i]; + } + + argv[gArgumentsCount] = NULL; + + PlatformDeinit(); + platformUartRestore(); + + alarm(0); + + execvp(argv[0], argv); + perror("reset failed"); + exit(EXIT_FAILURE); + +#endif // else OPENTHREAD_PLATFORM_USE_PSEUDO_RESET + + (void)aInstance; +} + +otPlatResetReason otPlatGetResetReason(otInstance *aInstance) +{ + (void)aInstance; + return sPlatResetReason; +} + +void otPlatWakeHost(void) +{ + // TODO: implement an operation to wake the host from sleep state. +} + +otError otPlatSetMcuPowerState(otInstance *aInstance, otPlatMcuPowerState aState) +{ + otError error = OT_ERROR_NONE; + + (void)aInstance; + + switch (aState) + { + case OT_PLAT_MCU_POWER_STATE_ON: + case OT_PLAT_MCU_POWER_STATE_LOW_POWER: + gPlatMcuPowerState = aState; + break; + + default: + error = OT_ERROR_FAILED; + break; + } + + return error; +} + +otPlatMcuPowerState otPlatGetMcuPowerState(otInstance *aInstance) +{ + (void)aInstance; + return gPlatMcuPowerState; +} diff --git a/src/posix/platform/openthread-core-posix-config.h b/src/posix/platform/openthread-core-posix-config.h new file mode 100644 index 000000000..0f664810b --- /dev/null +++ b/src/posix/platform/openthread-core-posix-config.h @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2017, 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 posix compile-time configuration constants + * for OpenThread. + */ + +#ifndef OPENTHREAD_CORE_POSIX_CONFIG_H_ +#define OPENTHREAD_CORE_POSIX_CONFIG_H_ + +/** + * @def OPENTHREAD_CONFIG_PLATFORM_INFO + * + * The platform-specific string to insert into the OpenThread version string. + * + */ +#define OPENTHREAD_CONFIG_PLATFORM_INFO "POSIX" + +/** + * @def OPENTHREAD_CONFIG_LOG_OUTPUT + * + * Specify where the log output should go. + * + */ +#ifndef OPENTHREAD_CONFIG_LOG_OUTPUT /* allow command line override */ +#define OPENTHREAD_CONFIG_LOG_OUTPUT OPENTHREAD_CONFIG_LOG_OUTPUT_PLATFORM_DEFINED +#endif + +#if OPENTHREAD_RADIO +/** + * @def OPENTHREAD_CONFIG_ENABLE_SOFTWARE_ACK_TIMEOUT + * + * Define to 1 if you want to enable software ACK timeout logic. + * + */ +#define OPENTHREAD_CONFIG_ENABLE_SOFTWARE_ACK_TIMEOUT 1 + +/** + * @def OPENTHREAD_CONFIG_ENABLE_SOFTWARE_RETRANSMIT + * + * Define to 1 if you want to enable software retransmission logic. + * + * Applicable only if raw link layer API is enabled (i.e., `OPENTHREAD_ENABLE_RAW_LINK_API` is set). + * + */ +#define OPENTHREAD_CONFIG_ENABLE_SOFTWARE_RETRANSMIT 1 +#endif // OPENTHREAD_RADIO + +/** + * @def OPENTHREAD_CONFIG_ENABLE_PLATFORM_USEC_TIMER + * + * Define to 1 if you want to support microsecond timer in platform. + * + */ +#define OPENTHREAD_CONFIG_ENABLE_PLATFORM_USEC_TIMER 1 + +#endif // OPENTHREAD_CORE_POSIX_CONFIG_H_ diff --git a/src/posix/platform/platform-posix.h b/src/posix/platform/platform-posix.h new file mode 100644 index 000000000..9ea6d949b --- /dev/null +++ b/src/posix/platform/platform-posix.h @@ -0,0 +1,213 @@ +/* + * 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 (posix or windows) platform-specific initializers. + */ + +#ifndef PLATFORM_POSIX_H_ +#define PLATFORM_POSIX_H_ + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "openthread-core-config.h" + +enum +{ + OT_SIM_EVENT_ALARM_FIRED = 0, + OT_SIM_EVENT_RADIO_RECEIVED = 1, + OT_EVENT_DATA_MAX_SIZE = 1024, +}; + +OT_TOOL_PACKED_BEGIN +struct Event +{ + uint64_t mDelay; + uint8_t mEvent; + uint16_t mDataLength; + uint8_t mData[OT_EVENT_DATA_MAX_SIZE]; +} OT_TOOL_PACKED_END; + +/** + * Unique node ID. + * + */ +extern uint64_t NODE_ID; + +/** + * Well-known Unique ID used by a simulated radio that supports promiscuous mode. + * + */ +#define WELLKNOWN_NODE_ID 34 + +/** + * This function initializes the alarm service used by OpenThread. + * + */ +void platformAlarmInit(uint32_t aSpeedUpFactor); + +/** + * This function retrieves the time remaining until the alarm fires. + * + * @param[out] aTimeval A pointer to the timeval struct. + * + */ +void platformAlarmUpdateTimeout(struct timeval *tv); + +/** + * This function performs alarm driver processing. + * + * @param[in] aInstance The OpenThread instance structure. + * + */ +void platformAlarmProcess(otInstance *aInstance); + +/** + * This function returns the next alarm event time. + * + * @returns The next alarm fire time. + * + */ +int32_t platformAlarmGetNext(void); + +/** + * This function returns the current alarm time. + * + * @returns The current alarm time. + * + */ +uint64_t platformAlarmGetNow(void); + +/** + * This function advances the alarm time by @p aDelta. + * + * @param[in] aDelta The amount of time to advance. + * + */ +void platformAlarmAdvanceNow(uint64_t aDelta); + +/** + * This function initializes the radio service used by OpenThread. + * + * @param[in] aRadioFile A pointer to the radio file. + * @param[in] aRadioConfig A pointer to the radio config. + * + */ +void platformRadioInit(const char *aRadioFile, const char *aRadioConfig); + +/** + * This function shuts down the radio service used by OpenThread. + * + */ +void platformRadioDeinit(void); + +/** + * This function inputs a received radio frame. + * + * @param[in] aInstance A pointer to the OpenThread instance. + * @param[in] aBuf A pointer to the received radio frame. + * @param[in] aBufLength The size of the received radio frame. + * + */ +void platformRadioReceive(otInstance *aInstance, uint8_t *aBuf, uint16_t aBufLength); + +/** + * This function updates the file descriptor sets with file descriptors used by the radio driver. + * + * @param[inout] aReadFdSet A pointer to the read file descriptors. + * @param[inout] aWriteFdSet A pointer to the write file descriptors. + * @param[inout] aMaxFd A pointer to the max file descriptor. + * @param[inout] aTimeout A pointer to the timeout. + * + */ +void platformRadioUpdateFdSet(fd_set *aReadFdSet, fd_set *aWriteFdSet, int *aMaxFd, struct timeval *aTimeout); + +/** + * This function performs radio driver processing. + * + * @param[in] aInstance A pointer to the OpenThread instance. + * @param[in] aReadFdSet A pointer to the read file descriptors. + * @param[in] aWriteFdSet A pointer to the write file descriptors. + * + */ +void platformRadioProcess(otInstance *aInstance, fd_set *aReadFdSet, fd_set *aWriteFdSet); + +/** + * This function initializes the random number service used by OpenThread. + * + */ +void platformRandomInit(void); + +/** + * This function updates the file descriptor sets with file descriptors used by the UART driver. + * + * @param[inout] aReadFdSet A pointer to the read file descriptors. + * @param[inout] aWriteFdSet A pointer to the write file descriptors. + * @param[inout] aMaxFd A pointer to the max file descriptor. + * + */ +void platformUartUpdateFdSet(fd_set *aReadFdSet, fd_set *aWriteFdSet, fd_set *aErrorFdSet, int *aMaxFd); + +/** + * This function performs radio driver processing. + * + * @param[in] aReadFdSet A pointer to the read file descriptors. + * @param[in] aWriteFdSet A pointer to the write file descriptors. + * @param[in] aErrorFdSet A pointer to the error file descriptors. + * + */ +void platformUartProcess(const fd_set *aReadFdSet, const fd_set *aWriteFdSet, const fd_set *aErrorFdSet); + +/** + * This function restores the Uart. + * + */ +void platformUartRestore(void); + +#endif // PLATFORM_POSIX_H_ diff --git a/src/posix/platform/platform.c b/src/posix/platform/platform.c new file mode 100644 index 000000000..019e6dac2 --- /dev/null +++ b/src/posix/platform/platform.c @@ -0,0 +1,162 @@ +/* + * 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-posix.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +uint64_t NODE_ID = 0; + +extern bool gPlatformPseudoResetWasRequested; + +int gArgumentsCount = 0; +char **gArguments = NULL; + +void PrintUsage(const char *aArg0) +{ + fprintf(stderr, "Syntax:\n %s [-s TimeSpeedUpFactor] {NodeId|Device DeviceConfig|Command CommandArgs}\n", aArg0); + exit(EXIT_FAILURE); +} + +void PlatformInit(int aArgCount, char *aArgVector[]) +{ + int i; + uint32_t speedUpFactor = 1; + char * endptr; + const char *radioFile = NULL; + const char *radioConfig = ""; + + if (gPlatformPseudoResetWasRequested) + { + gPlatformPseudoResetWasRequested = false; + return; + } + + if (aArgCount < 2) + { + PrintUsage(aArgVector[0]); + } + + i = 1; + if (!strcmp(aArgVector[i], "-s")) + { + ++i; + speedUpFactor = (uint32_t)strtol(aArgVector[i], &endptr, 0); + + if (*endptr != '\0' || speedUpFactor == 0) + { + fprintf(stderr, "Invalid value for TimerSpeedUpFactor: %s\n", aArgVector[i]); + exit(EXIT_FAILURE); + } + ++i; + } + + if (i >= aArgCount) + { + PrintUsage(aArgVector[0]); + } + + radioFile = aArgVector[i]; + if (i + 1 < aArgCount) + { + radioConfig = aArgVector[i + 1]; + } + + openlog(basename(aArgVector[0]), LOG_PID, LOG_USER); + setlogmask(setlogmask(0) & LOG_UPTO(LOG_NOTICE)); + + gArgumentsCount = aArgCount; + gArguments = aArgVector; + + platformAlarmInit(speedUpFactor); + platformRadioInit(radioFile, radioConfig); + platformRandomInit(); +} + +bool PlatformPseudoResetWasRequested(void) +{ + return gPlatformPseudoResetWasRequested; +} + +void PlatformDeinit(void) +{ + platformRadioDeinit(); +} + +void PlatformProcessDrivers(otInstance *aInstance) +{ + fd_set read_fds; + fd_set write_fds; + fd_set error_fds; + struct timeval timeout; + int max_fd = -1; + int rval; + + FD_ZERO(&read_fds); + FD_ZERO(&write_fds); + FD_ZERO(&error_fds); + + platformAlarmUpdateTimeout(&timeout); + platformUartUpdateFdSet(&read_fds, &write_fds, &error_fds, &max_fd); + platformRadioUpdateFdSet(&read_fds, &write_fds, &max_fd, &timeout); + + if (otTaskletsArePending(aInstance)) + { + timeout.tv_sec = 0; + timeout.tv_usec = 0; + } + + rval = select(max_fd + 1, &read_fds, &write_fds, &error_fds, &timeout); + + if ((rval < 0) && (errno != EINTR)) + { + perror("select"); + exit(EXIT_FAILURE); + } + + platformUartProcess(&read_fds, &write_fds, &error_fds); + platformRadioProcess(aInstance, &read_fds, &write_fds); + platformAlarmProcess(aInstance); +} diff --git a/src/posix/platform/platform.h b/src/posix/platform/platform.h new file mode 100644 index 000000000..c1378526b --- /dev/null +++ b/src/posix/platform/platform.h @@ -0,0 +1,82 @@ +/* + * 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 defines the platform-specific initializers. + */ + +#ifndef PLATFORM_H_ +#define PLATFORM_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * This function performs all platform-specific initialization. + * + */ +void PlatformInit(int argc, char *argv[]); + +/** + * This function performs all platform-specific deinitialization. + * + */ +void PlatformDeinit(void); + +/** + * This function returns true is a pseudo-reset was requested. + * In such a case, the main loop should shut down and re-initialize + * the OpenThread instance. + * + */ +bool PlatformPseudoResetWasRequested(void); + +/** + * This function performs all platform-specific processing. + * + * @param[in] aInstance The OpenThread instance structure. + * + */ +void PlatformProcessDrivers(otInstance *aInstance); + +/** + * This function is called whenever platform drivers needs processing. + * + */ +extern void PlatformEventSignalPending(void); + +#ifdef __cplusplus +} // end of extern "C" +#endif + +#endif // PLATFORM_H_ diff --git a/src/posix/platform/radio_spinel.cpp b/src/posix/platform/radio_spinel.cpp new file mode 100644 index 000000000..281f89408 --- /dev/null +++ b/src/posix/platform/radio_spinel.cpp @@ -0,0 +1,1588 @@ +/* + * Copyright (c) 2018, 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 spinel based radio transceiver. + */ + +extern "C" { +#include "platform-posix.h" +} + +#include "radio_spinel.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#ifndef SOCKET_UTILS_DEFAULT_SHELL +#define SOCKET_UTILS_DEFAULT_SHELL "/bin/sh" +#endif + +enum +{ + IEEE802154_MIN_LENGTH = 5, + IEEE802154_MAX_LENGTH = 127, + IEEE802154_ACK_LENGTH = 5, + + IEEE802154_BROADCAST = 0xffff, + + IEEE802154_FRAME_TYPE_ACK = 2 << 0, + IEEE802154_FRAME_TYPE_MACCMD = 3 << 0, + IEEE802154_FRAME_TYPE_MASK = 7 << 0, + + IEEE802154_SECURITY_ENABLED = 1 << 3, + IEEE802154_FRAME_PENDING = 1 << 4, + IEEE802154_ACK_REQUEST = 1 << 5, + IEEE802154_PANID_COMPRESSION = 1 << 6, + + IEEE802154_DST_ADDR_NONE = 0 << 2, + IEEE802154_DST_ADDR_SHORT = 2 << 2, + IEEE802154_DST_ADDR_EXT = 3 << 2, + IEEE802154_DST_ADDR_MASK = 3 << 2, + + IEEE802154_SRC_ADDR_NONE = 0 << 6, + IEEE802154_SRC_ADDR_SHORT = 2 << 6, + IEEE802154_SRC_ADDR_EXT = 3 << 6, + IEEE802154_SRC_ADDR_MASK = 3 << 6, + + IEEE802154_DSN_OFFSET = 2, + IEEE802154_DSTPAN_OFFSET = 3, + IEEE802154_DSTADDR_OFFSET = 5, + + IEEE802154_SEC_LEVEL_MASK = 7 << 0, + + IEEE802154_KEY_ID_MODE_0 = 0 << 3, + IEEE802154_KEY_ID_MODE_1 = 1 << 3, + IEEE802154_KEY_ID_MODE_2 = 2 << 3, + IEEE802154_KEY_ID_MODE_3 = 3 << 3, + IEEE802154_KEY_ID_MODE_MASK = 3 << 3, + + IEEE802154_MACCMD_DATA_REQ = 4, +}; + +enum +{ + kIdle, + kSent, + kDone, +}; + +static ot::RadioSpinel sRadioSpinel; + +static inline otPanId getDstPan(const uint8_t *frame) +{ + return static_cast((frame[IEEE802154_DSTPAN_OFFSET + 1] << 8) | frame[IEEE802154_DSTPAN_OFFSET]); +} + +static inline otShortAddress getShortAddress(const uint8_t *frame) +{ + return static_cast((frame[IEEE802154_DSTADDR_OFFSET + 1] << 8) | frame[IEEE802154_DSTADDR_OFFSET]); +} + +static inline void getExtAddress(const uint8_t *frame, otExtAddress *address) +{ + size_t i; + + for (i = 0; i < sizeof(otExtAddress); i++) + { + address->m8[i] = frame[IEEE802154_DSTADDR_OFFSET + (sizeof(otExtAddress) - 1 - i)]; + } +} + +static inline bool isAckRequested(const uint8_t *frame) +{ + return (frame[0] & IEEE802154_ACK_REQUEST) != 0; +} + +static inline void SuccessOrDie(otError aError) +{ + if (aError != OT_ERROR_NONE) + { + exit(EXIT_FAILURE); + } +} + +namespace ot { + +/** + * This function returns if the property changed event is *UNSAFE* to be handled during `WaitResponse()`. + * + * If property could trigger another call to `ncpSet()`, it's unsafe. + * + * @param[in] aKey The identifier of the property. + * + * @returns Whether this property is *UNSAFE* to be handled during 'WaitResponse()`. + * + */ +static bool ShouldDefer(spinel_prop_key_t aKey) +{ + return aKey == SPINEL_PROP_STREAM_RAW || aKey == SPINEL_PROP_MAC_ENERGY_SCAN_RESULT; +} + +static otError SpinelStatusToOtError(spinel_status_t aError) +{ + otError ret; + + switch (aError) + { + case SPINEL_STATUS_OK: + ret = OT_ERROR_NONE; + break; + + case SPINEL_STATUS_FAILURE: + ret = OT_ERROR_FAILED; + break; + + case SPINEL_STATUS_DROPPED: + ret = OT_ERROR_DROP; + break; + + case SPINEL_STATUS_NOMEM: + ret = OT_ERROR_NO_BUFS; + break; + + case SPINEL_STATUS_BUSY: + ret = OT_ERROR_BUSY; + break; + + case SPINEL_STATUS_PARSE_ERROR: + ret = OT_ERROR_PARSE; + break; + + case SPINEL_STATUS_INVALID_ARGUMENT: + ret = OT_ERROR_INVALID_ARGS; + break; + + case SPINEL_STATUS_UNIMPLEMENTED: + ret = OT_ERROR_NOT_IMPLEMENTED; + break; + + case SPINEL_STATUS_INVALID_STATE: + ret = OT_ERROR_INVALID_STATE; + break; + + case SPINEL_STATUS_NO_ACK: + ret = OT_ERROR_NO_ACK; + break; + + case SPINEL_STATUS_CCA_FAILURE: + ret = OT_ERROR_CHANNEL_ACCESS_FAILURE; + break; + + case SPINEL_STATUS_ALREADY: + ret = OT_ERROR_ALREADY; + break; + + case SPINEL_STATUS_PROP_NOT_FOUND: + case SPINEL_STATUS_ITEM_NOT_FOUND: + ret = OT_ERROR_NOT_FOUND; + break; + + default: + if (aError >= SPINEL_STATUS_STACK_NATIVE__BEGIN && aError <= SPINEL_STATUS_STACK_NATIVE__END) + { + ret = static_cast(aError - SPINEL_STATUS_STACK_NATIVE__BEGIN); + } + else + { + ret = OT_ERROR_FAILED; + } + break; + } + + return ret; +} + +static void LogIfFail(otInstance *aInstance, const char *aText, otError aError) +{ + if (aError != OT_ERROR_NONE) + { + otLogWarnPlat(aInstance, "%s: %s", aText, otThreadErrorToString(aError)); + } + + OT_UNUSED_VARIABLE(aInstance); + OT_UNUSED_VARIABLE(aText); + OT_UNUSED_VARIABLE(aError); +} + +static int OpenPty(const char *aFile, const char *aConfig) +{ + int fd = -1; + int pid = -1; + + { + struct termios tios; + + memset(&tios, 0, sizeof(tios)); + cfmakeraw(&tios); + tios.c_cflag = CS8 | HUPCL | CREAD | CLOCAL; + + pid = forkpty(&fd, NULL, &tios, NULL); + VerifyOrExit(pid >= 0); + } + + if (0 == pid) + { + const int kMaxCommand = 255; + char cmd[kMaxCommand]; + int rval; + const int dtablesize = getdtablesize(); + + rval = setenv("SHELL", SOCKET_UTILS_DEFAULT_SHELL, 0); + + VerifyOrExit(rval == 0, perror("setenv failed")); + + // Close all file descriptors larger than STDERR_FILENO. + for (int i = (STDERR_FILENO + 1); i < dtablesize; i++) + { + close(i); + } + + rval = snprintf(cmd, sizeof(cmd), "%s %s", aFile, aConfig); + VerifyOrExit(rval > 0 && static_cast(rval) < sizeof(cmd), + otLogCritPlat(mInstance, "NCP file and configuration is too long!")); + + execl(getenv("SHELL"), getenv("SHELL"), "-c", cmd, NULL); + perror("open pty failed"); + + exit(EXIT_FAILURE); + } + else + { + int rval = fcntl(fd, F_GETFL); + + if (rval != -1) + { + rval = fcntl(fd, F_SETFL, rval | O_NONBLOCK); + } + + if (rval == -1) + { + perror("set nonblock failed"); + close(fd); + fd = -1; + } + } + +exit: + return fd; +} + +static int OpenUart(const char *aRadioFile, const char *aRadioConfig) +{ + const int kMaxSttyCommand = 128; + char cmd[kMaxSttyCommand]; + int fd = -1; + int rval; + + VerifyOrExit(!strchr(aRadioConfig, '&') && !strchr(aRadioConfig, '|') && !strchr(aRadioConfig, ';'), + otLogCritPlat(mInstance, "Illegal NCP config arguments!")); + + rval = snprintf(cmd, sizeof(cmd), "stty -F %s %s", aRadioFile, aRadioConfig); + VerifyOrExit(rval > 0 && static_cast(rval) < sizeof(cmd), + otLogCritPlat(mInstance, "NCP file and configuration is too long!")); + + rval = system(cmd); + VerifyOrExit(rval == 0, otLogCritPlat(mInstance, "Unable to configure serial port")); + + fd = open(aRadioFile, O_RDWR | O_NOCTTY | O_NONBLOCK); + if (fd == -1) + { + perror("open uart failed"); + ExitNow(); + } + + rval = tcflush(fd, TCIOFLUSH); + VerifyOrExit(rval == 0, otLogCritPlat(mInstance, "Unable to flush serial port")); + +exit: + return fd; +} + +class UartTxBuffer : public Hdlc::Encoder::BufferWriteIterator +{ +public: + UartTxBuffer(void) + { + mWritePointer = mBuffer; + mRemainingLength = sizeof(mBuffer); + } + + uint16_t GetLength(void) const { return static_cast(mWritePointer - mBuffer); } + const uint8_t *GetBuffer(void) const { return mBuffer; } + +private: + enum + { + kUartTxBufferSize = 512, // Uart tx buffer size. + }; + uint8_t mBuffer[kUartTxBufferSize]; +}; + +RadioSpinel::RadioSpinel(void) + : mCmdTidsInUse(0) + , mCmdNextTid(1) + , mTxRadioTid(0) + , mWaitingTid(0) + , mWaitingKey(SPINEL_PROP_LAST_STATUS) + , mHdlcDecoder(mHdlcBuffer, sizeof(mHdlcBuffer), HandleSpinelFrame, HandleHdlcError, this) + , mRxSensitivity(0) + , mTxState(kIdle) + , mSockFd(-1) + , mState(OT_RADIO_STATE_DISABLED) + , mAckWait(false) + , mPromiscuous(false) + , mIsReady(false) +{ +} + +void RadioSpinel::Init(const char *aRadioFile, const char *aRadioConfig) +{ + otError error = OT_ERROR_NONE; + struct stat st; + + // not allowed to initialize again. + assert(mSockFd == -1); + + VerifyOrExit(stat(aRadioFile, &st) == 0, perror("stat ncp file failed")); + + if (S_ISCHR(st.st_mode)) + { + mSockFd = OpenUart(aRadioFile, aRadioConfig); + } + else if (S_ISREG(st.st_mode)) + { + mSockFd = OpenPty(aRadioFile, aRadioConfig); + } + + VerifyOrExit(mSockFd != -1, error = OT_ERROR_INVALID_ARGS); + error = SendReset(); + VerifyOrExit(error == OT_ERROR_NONE); + error = WaitResponse(); + VerifyOrExit(error == OT_ERROR_NONE); + VerifyOrExit(mIsReady, error = OT_ERROR_FAILED); + + if (OT_ERROR_NONE != Get(SPINEL_PROP_HWADDR, SPINEL_DATATYPE_UINT64_S, &NODE_ID)) + { + exit(EXIT_FAILURE); + } + + assert(mSockFd != -1); + + mRxRadioFrame.mPsdu = mRxPsdu; + mTxRadioFrame.mPsdu = mTxPsdu; + +exit: + if (error != OT_ERROR_NONE) + { + exit(EXIT_FAILURE); + } +} + +void RadioSpinel::Deinit(void) +{ + // this function is only allowed after successfully initialized. + assert(mSockFd != -1); + + VerifyOrExit(0 == close(mSockFd), perror("close NCP")); + VerifyOrExit(-1 != wait(NULL), perror("wait NCP")); + +exit: + return; +} + +void RadioSpinel::HandleSpinelFrame(const uint8_t *aBuffer, uint16_t aLength) +{ + otError error = OT_ERROR_NONE; + uint8_t header; + spinel_ssize_t rval; + + rval = spinel_datatype_unpack(aBuffer, aLength, "C", &header); + + VerifyOrExit(rval > 0 && (header & SPINEL_HEADER_FLAG) == SPINEL_HEADER_FLAG && SPINEL_HEADER_GET_IID(header) == 0, + error = OT_ERROR_PARSE); + + if (SPINEL_HEADER_GET_TID(header) == 0) + { + HandleNotification(aBuffer, aLength); + } + else + { + HandleResponse(aBuffer, aLength); + } + +exit: + LogIfFail(mInstance, "Error handling hdlc frame", error); +} + +void RadioSpinel::HandleHdlcError(void *aContext, otError aError, uint8_t *aBuffer, uint16_t aLength) +{ + otLogWarnPlat(static_cast(aContext)->mInstance, "Error decoding hdlc frame: %s", + otThreadErrorToString(aError)); + OT_UNUSED_VARIABLE(aContext); + OT_UNUSED_VARIABLE(aError); + OT_UNUSED_VARIABLE(aBuffer); + OT_UNUSED_VARIABLE(aLength); +} + +void RadioSpinel::HandleNotification(const uint8_t *aBuffer, uint16_t aLength) +{ + spinel_prop_key_t key; + spinel_size_t len = 0; + spinel_ssize_t unpacked; + uint8_t * data = NULL; + uint32_t cmd; + uint8_t header; + otError error = OT_ERROR_NONE; + + unpacked = spinel_datatype_unpack(aBuffer, aLength, "CiiD", &header, &cmd, &key, &data, &len); + VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE); + VerifyOrExit(SPINEL_HEADER_GET_TID(header) == 0, error = OT_ERROR_PARSE); + + switch (cmd) + { + case SPINEL_CMD_PROP_VALUE_IS: + // Some spinel properties cannot be handled during `WaitResponse()`, we must cache these events. + // `mWaitingTid` is released immediately after received the response. And `mWaitingKey` is be set + // to `SPINEL_PROP_LAST_STATUS` at the end of `WaitResponse()`. + VerifyOrExit(mWaitingKey == SPINEL_PROP_LAST_STATUS || !ShouldDefer(key), + error = mFrameQueue.Push(aBuffer, aLength)); + HandleValueIs(key, data, static_cast(len)); + break; + + case SPINEL_CMD_PROP_VALUE_INSERTED: + case SPINEL_CMD_PROP_VALUE_REMOVED: + otLogInfoPlat(mInstance, "Ignored command %d", cmd); + break; + + default: + ExitNow(error = OT_ERROR_PARSE); + } + +exit: + LogIfFail(mInstance, "Error processing notification", error); +} + +void RadioSpinel::HandleResponse(const uint8_t *aBuffer, uint16_t aLength) +{ + spinel_prop_key_t key; + uint8_t * data = NULL; + spinel_size_t len = 0; + uint8_t header = 0; + uint32_t cmd = 0; + spinel_ssize_t rval = 0; + otError error = OT_ERROR_NONE; + + rval = spinel_datatype_unpack(aBuffer, aLength, "CiiD", &header, &cmd, &key, &data, &len); + VerifyOrExit(rval > 0 && cmd >= SPINEL_CMD_PROP_VALUE_IS && cmd <= SPINEL_CMD_PROP_VALUE_REMOVED, + error = OT_ERROR_PARSE); + + if (mWaitingTid == SPINEL_HEADER_GET_TID(header)) + { + HandleWaitingResponse(cmd, key, data, static_cast(len)); + FreeTid(mWaitingTid); + mWaitingTid = 0; + } + else if (mTxRadioTid == SPINEL_HEADER_GET_TID(header)) + { + HandleTransmitDone(cmd, key, data, static_cast(len)); + FreeTid(mTxRadioTid); + mTxRadioTid = 0; + } + else + { + otLogWarnPlat(mInstance, "Unexpected Spinel transaction message: %u", SPINEL_HEADER_GET_TID(header)); + error = OT_ERROR_DROP; + } + +exit: + LogIfFail(mInstance, "Error processing response", error); +} + +void RadioSpinel::HandleWaitingResponse(uint32_t aCommand, + spinel_prop_key_t aKey, + const uint8_t * aBuffer, + uint16_t aLength) +{ + if (aKey == SPINEL_PROP_LAST_STATUS) + { + spinel_status_t status; + spinel_ssize_t unpacked = spinel_datatype_unpack(aBuffer, aLength, "i", &status); + + VerifyOrExit(unpacked > 0, mError = OT_ERROR_PARSE); + mError = SpinelStatusToOtError(status); + } + else if (aKey == mWaitingKey) + { + if (mPropertyFormat) + { + spinel_ssize_t unpacked = + spinel_datatype_vunpack_in_place(aBuffer, aLength, mPropertyFormat, mPropertyArgs); + + VerifyOrExit(unpacked > 0, mError = OT_ERROR_PARSE); + mError = OT_ERROR_NONE; + } + else + { + if (aCommand == mExpectedCommand) + { + mError = OT_ERROR_NONE; + } + else + { + mError = OT_ERROR_DROP; + } + } + } + else + { + mError = OT_ERROR_DROP; + } + +exit: + LogIfFail(mInstance, "Error processing result", mError); +} + +void RadioSpinel::HandleValueIs(spinel_prop_key_t aKey, const uint8_t *aBuffer, uint16_t aLength) +{ + otError error = OT_ERROR_NONE; + + if (aKey == SPINEL_PROP_STREAM_RAW) + { + SuccessOrExit(error = ParseRadioFrame(mRxRadioFrame, aBuffer, aLength)); + RadioReceive(); + } + else if (aKey == SPINEL_PROP_LAST_STATUS) + { + spinel_status_t status = SPINEL_STATUS_OK; + spinel_ssize_t unpacked; + + unpacked = spinel_datatype_unpack(aBuffer, aLength, "i", &status); + VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE); + + if (status >= SPINEL_STATUS_RESET__BEGIN && status <= SPINEL_STATUS_RESET__END) + { + otLogInfoPlat(mInstance, "NCP reset: %s", spinel_status_to_cstr(status)); + mIsReady = true; + } + else + { + otLogInfoPlat(mInstance, "NCP last status: %s", spinel_status_to_cstr(status)); + } + } + else if (aKey == SPINEL_PROP_MAC_ENERGY_SCAN_RESULT) + { + uint8_t scanChannel; + int8_t maxRssi; + spinel_ssize_t unpacked; + + unpacked = spinel_datatype_unpack(aBuffer, aLength, "Cc", &scanChannel, &maxRssi); + + VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE); +#if !OPENTHREAD_ENABLE_DIAG + otPlatRadioEnergyScanDone(aInstance, maxRssi); +#endif + } + else if (aKey == SPINEL_PROP_STREAM_DEBUG) + { + const char * message = NULL; + spinel_ssize_t unpacked; + + unpacked = spinel_datatype_unpack(aBuffer, aLength, SPINEL_DATATYPE_UTF8_S, &message); + VerifyOrExit(unpacked > 0 && message, error = OT_ERROR_PARSE); + otLogDebgPlat(mInstance, "NCP DEBUG INFO: %s", message); + } + +exit: + LogIfFail(mInstance, "Failed to handle ValueIs", error); +} + +otError RadioSpinel::ParseRadioFrame(otRadioFrame &aFrame, const uint8_t *aBuffer, uint16_t aLength) +{ + otError error = OT_ERROR_NONE; + uint16_t packetLength = 0; + spinel_ssize_t unpacked; + uint16_t flags = 0; + int8_t noiseFloor = -128; + spinel_size_t size = OT_RADIO_FRAME_MAX_SIZE; + + unpacked = spinel_datatype_unpack(aBuffer, aLength, SPINEL_DATATYPE_UINT16_S, &packetLength); + VerifyOrExit(unpacked > 0 && packetLength <= OT_RADIO_FRAME_MAX_SIZE, error = OT_ERROR_PARSE); + + aFrame.mLength = static_cast(packetLength); + + unpacked = spinel_datatype_unpack_in_place(aBuffer, aLength, + SPINEL_DATATYPE_DATA_WLEN_S SPINEL_DATATYPE_INT8_S SPINEL_DATATYPE_INT8_S + SPINEL_DATATYPE_UINT16_S SPINEL_DATATYPE_STRUCT_S( // PHY-data + SPINEL_DATATYPE_UINT8_S // 802.15.4 channel + SPINEL_DATATYPE_UINT8_S // 802.15.4 LQI + ), + aFrame.mPsdu, &size, &aFrame.mInfo.mRxInfo.mRssi, &noiseFloor, &flags, + &aFrame.mChannel, &aFrame.mInfo.mRxInfo.mLqi); + VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE); + +exit: + LogIfFail(mInstance, "Handle radio frame failed", error); + return error; +} + +void RadioSpinel::ReadAll(void) +{ + uint8_t buf[kMaxSpinelFrame]; + ssize_t rval = read(mSockFd, buf, sizeof(buf)); + + if (rval < 0) + { + perror("read spinel"); + if (errno != EAGAIN) + { + abort(); + } + } + + if (rval > 0) + { + mHdlcDecoder.Decode(buf, static_cast(rval)); + } +} + +void RadioSpinel::ProcessFrameQueue(void) +{ + uint8_t length; + uint8_t buffer[kMaxSpinelFrame]; + const uint8_t *frame; + + while ((frame = mFrameQueue.Peek(buffer, length)) != NULL) + { + HandleNotification(frame, length); + mFrameQueue.Shift(); + } +} + +void RadioSpinel::RadioReceive(void) +{ + otError error = OT_ERROR_NONE; + otPanId dstpan; + otShortAddress shortAddress; + otExtAddress extAddress; + + VerifyOrExit(mPromiscuous == false, error = OT_ERROR_NONE); + VerifyOrExit((mState == OT_RADIO_STATE_RECEIVE || mState == OT_RADIO_STATE_TRANSMIT), error = OT_ERROR_DROP); + + switch (mRxRadioFrame.mPsdu[1] & IEEE802154_DST_ADDR_MASK) + { + case IEEE802154_DST_ADDR_NONE: + break; + + case IEEE802154_DST_ADDR_SHORT: + dstpan = getDstPan(mRxRadioFrame.mPsdu); + shortAddress = getShortAddress(mRxRadioFrame.mPsdu); + VerifyOrExit((dstpan == IEEE802154_BROADCAST || dstpan == mPanid) && + (shortAddress == IEEE802154_BROADCAST || shortAddress == mShortAddress), + error = OT_ERROR_ABORT); + break; + + case IEEE802154_DST_ADDR_EXT: + dstpan = getDstPan(mRxRadioFrame.mPsdu); + getExtAddress(mRxRadioFrame.mPsdu, &extAddress); + VerifyOrExit((dstpan == IEEE802154_BROADCAST || dstpan == mPanid) && + memcmp(&extAddress, mExtendedAddress.m8, sizeof(extAddress)) == 0, + error = OT_ERROR_ABORT); + break; + + default: + error = OT_ERROR_ABORT; + goto exit; + } + +exit: + +#if OPENTHREAD_ENABLE_DIAG + + if (otPlatDiagModeGet()) + { + otPlatDiagRadioReceiveDone(mInstance, error == OT_ERROR_NONE ? &mRxRadioFrame : NULL, error); + } + else +#endif + { + otPlatRadioReceiveDone(mInstance, error == OT_ERROR_NONE ? &mRxRadioFrame : NULL, error); + } +} + +void RadioSpinel::UpdateFdSet(fd_set &aReadFdSet, fd_set &aWriteFdSet, int &aMaxFd, struct timeval &aTimeout) +{ + if ((mState != OT_RADIO_STATE_TRANSMIT || mTxState == kSent)) + { + FD_SET(mSockFd, &aReadFdSet); + + if (aMaxFd < mSockFd) + { + aMaxFd = mSockFd; + } + } + + if (mState == OT_RADIO_STATE_TRANSMIT && mTxState == kIdle) + { + FD_SET(mSockFd, &aWriteFdSet); + + if (aMaxFd < mSockFd) + { + aMaxFd = mSockFd; + } + } + + if (!mFrameQueue.IsEmpty()) + { + aTimeout.tv_sec = 0; + aTimeout.tv_usec = 0; + } +} + +void RadioSpinel::Process(const fd_set &aReadFdSet, const fd_set &aWriteFdSet) +{ + if (FD_ISSET(mSockFd, &aReadFdSet) || !mFrameQueue.IsEmpty()) + { + ProcessFrameQueue(); + + if (FD_ISSET(mSockFd, &aReadFdSet)) + { + ReadAll(); + ProcessFrameQueue(); + } + + if (mState == OT_RADIO_STATE_TRANSMIT && mTxState == kDone) + { + mState = OT_RADIO_STATE_RECEIVE; + otPlatRadioTxDone(mInstance, mTransmitFrame, (mAckWait ? &mRxRadioFrame : NULL), mTxError); + } + } + + if (FD_ISSET(mSockFd, &aWriteFdSet)) + { + if (mState == OT_RADIO_STATE_TRANSMIT && mTxState == kIdle) + { + RadioTransmit(); + } + } +} + +otError RadioSpinel::SetPromiscuous(bool aEnable) +{ + otError error; + + uint8_t mode = (aEnable ? SPINEL_MAC_PROMISCUOUS_MODE_NETWORK : SPINEL_MAC_PROMISCUOUS_MODE_OFF); + SuccessOrExit(error = Set(SPINEL_PROP_MAC_PROMISCUOUS_MODE, SPINEL_DATATYPE_UINT8_S, mode)); + mPromiscuous = aEnable; + +exit: + return error; +} + +otError RadioSpinel::SetShortAddress(uint16_t aAddress) +{ + otError error; + + SuccessOrExit(error = sRadioSpinel.Set(SPINEL_PROP_MAC_15_4_SADDR, SPINEL_DATATYPE_UINT16_S, aAddress)); + mShortAddress = aAddress; + +exit: + return error; +} + +otError RadioSpinel::GetIeeeEui64(uint8_t *aIeeeEui64) +{ + return Get(SPINEL_PROP_HWADDR, SPINEL_DATATYPE_EUI64_S, aIeeeEui64); +} + +otError RadioSpinel::SetExtendedAddress(const otExtAddress &aExtAddress) +{ + otError error; + + SuccessOrExit(error = Set(SPINEL_PROP_MAC_15_4_LADDR, SPINEL_DATATYPE_EUI64_S, aExtAddress.m8)); + mExtendedAddress = aExtAddress; + +exit: + return error; +} + +otError RadioSpinel::SetPanId(uint16_t aPanId) +{ + otError error; + SuccessOrExit(error = Set(SPINEL_PROP_MAC_15_4_PANID, SPINEL_DATATYPE_UINT16_S, aPanId)); + mPanid = aPanId; + +exit: + return error; +} + +otError RadioSpinel::EnableSrcMatch(bool aEnable) +{ + return Set(SPINEL_PROP_MAC_SRC_MATCH_ENABLED, SPINEL_DATATYPE_BOOL_S, aEnable); +} + +otError RadioSpinel::AddSrcMatchShortEntry(const uint16_t aShortAddress) +{ + return Insert(SPINEL_PROP_MAC_SRC_MATCH_SHORT_ADDRESSES, SPINEL_DATATYPE_UINT16_S, aShortAddress); +} + +otError RadioSpinel::AddSrcMatchExtEntry(const otExtAddress &aExtAddress) +{ + return Insert(SPINEL_PROP_MAC_SRC_MATCH_EXTENDED_ADDRESSES, SPINEL_DATATYPE_EUI64_S, aExtAddress.m8); +} + +otError RadioSpinel::ClearSrcMatchShortEntry(const uint16_t aShortAddress) +{ + return Remove(SPINEL_PROP_MAC_SRC_MATCH_SHORT_ADDRESSES, SPINEL_DATATYPE_UINT16_S, aShortAddress); +} + +otError RadioSpinel::ClearSrcMatchExtEntry(const otExtAddress &aExtAddress) +{ + return Remove(SPINEL_PROP_MAC_SRC_MATCH_EXTENDED_ADDRESSES, SPINEL_DATATYPE_EUI64_S, aExtAddress.m8); +} + +otError RadioSpinel::ClearSrcMatchShortEntries(void) +{ + return Set(SPINEL_PROP_MAC_SRC_MATCH_SHORT_ADDRESSES, NULL); +} + +otError RadioSpinel::ClearSrcMatchExtEntries(void) +{ + return Set(SPINEL_PROP_MAC_SRC_MATCH_EXTENDED_ADDRESSES, NULL); +} + +otError RadioSpinel::GetTransmitPower(int8_t &aPower) +{ + otError error = Get(SPINEL_PROP_PHY_TX_POWER, SPINEL_DATATYPE_INT8_S, aPower); + + LogIfFail(mInstance, "Get transmit power failed", error); + return error; +} + +otError RadioSpinel::SetTransmitPower(int8_t aPower) +{ + otError error = Set(SPINEL_PROP_PHY_TX_POWER, SPINEL_DATATYPE_INT8_S, aPower); + LogIfFail(mInstance, "Set transmit power failed", error); + return error; +} + +otError RadioSpinel::Get(spinel_prop_key_t aKey, const char *aFormat, ...) +{ + otError error; + + assert(mWaitingTid == 0); + + mPropertyFormat = aFormat; + va_start(mPropertyArgs, aFormat); + error = RequestV(true, SPINEL_CMD_PROP_VALUE_GET, aKey, NULL, mPropertyArgs); + va_end(mPropertyArgs); + mPropertyFormat = NULL; + + return error; +} + +otError RadioSpinel::Set(spinel_prop_key_t aKey, const char *aFormat, ...) +{ + otError error; + + assert(mWaitingTid == 0); + + mExpectedCommand = SPINEL_CMD_PROP_VALUE_IS; + va_start(mPropertyArgs, aFormat); + error = RequestV(true, SPINEL_CMD_PROP_VALUE_SET, aKey, aFormat, mPropertyArgs); + va_end(mPropertyArgs); + mExpectedCommand = SPINEL_CMD_NOOP; + + return error; +} + +otError RadioSpinel::Insert(spinel_prop_key_t aKey, const char *aFormat, ...) +{ + otError error; + + assert(mWaitingTid == 0); + + mExpectedCommand = SPINEL_CMD_PROP_VALUE_INSERTED; + va_start(mPropertyArgs, aFormat); + error = RequestV(true, SPINEL_CMD_PROP_VALUE_INSERT, aKey, aFormat, mPropertyArgs); + va_end(mPropertyArgs); + mExpectedCommand = SPINEL_CMD_NOOP; + + return error; +} + +otError RadioSpinel::Remove(spinel_prop_key_t aKey, const char *aFormat, ...) +{ + otError error; + + assert(mWaitingTid == 0); + + mExpectedCommand = SPINEL_CMD_PROP_VALUE_REMOVED; + va_start(mPropertyArgs, aFormat); + error = RequestV(true, SPINEL_CMD_PROP_VALUE_REMOVE, aKey, aFormat, mPropertyArgs); + va_end(mPropertyArgs); + mExpectedCommand = SPINEL_CMD_NOOP; + + return error; +} + +otError RadioSpinel::WaitResponse(void) +{ + otError error = OT_ERROR_NONE; + struct timeval end; + struct timeval now; + struct timeval timeout = {kMaxWaitTime / 1000, (kMaxWaitTime % 1000) * 1000}; + + gettimeofday(&now, NULL); + timeradd(&now, &timeout, &end); + + do + { + fd_set read_fds; + fd_set error_fds; + int rval; + + FD_ZERO(&read_fds); + FD_ZERO(&error_fds); + FD_SET(mSockFd, &read_fds); + FD_SET(mSockFd, &error_fds); + + rval = select(mSockFd + 1, &read_fds, NULL, &error_fds, &timeout); + + if (rval > 0) + { + if (FD_ISSET(mSockFd, &read_fds)) + { + ReadAll(); + } + else if (FD_ISSET(mSockFd, &error_fds)) + { + exit(EXIT_FAILURE); + } + else + { + assert(false); + exit(EXIT_FAILURE); + } + } + else if (rval == 0) + { + FreeTid(mWaitingTid); + mWaitingTid = 0; + ExitNow(mError = OT_ERROR_RESPONSE_TIMEOUT); + } + else if (errno != EINTR) + { + perror("wait response"); + exit(EXIT_FAILURE); + } + + gettimeofday(&now, NULL); + if (timercmp(&end, &now, >)) + { + timersub(&end, &now, &timeout); + } + else + { + mWaitingTid = 0; + mError = OT_ERROR_RESPONSE_TIMEOUT; + } + } while (mWaitingTid || !mIsReady); + + error = mError; + +exit: + LogIfFail(mInstance, "Error waiting response", error); + // This indicates end of waiting repsonse. + mWaitingKey = SPINEL_PROP_LAST_STATUS; + return error; +} + +spinel_tid_t RadioSpinel::GetNextTid(void) +{ + spinel_tid_t tid = 0; + + if (((1 << mCmdNextTid) & mCmdTidsInUse) == 0) + { + tid = mCmdNextTid; + mCmdNextTid = SPINEL_GET_NEXT_TID(mCmdNextTid); + mCmdTidsInUse |= (1 << tid); + } + + return tid; +} + +/** + * This method delievers the radio frame to transceiver. + * + * otPlatRadioTxStarted() is triggered immediately for now, which may be earlier than real started time. + * + */ +void RadioSpinel::RadioTransmit(void) +{ + otError error; + + assert(mTransmitFrame != NULL); + otPlatRadioTxStarted(mInstance, mTransmitFrame); + assert(mTxState == kIdle); + + mAckWait = isAckRequested(mTransmitFrame->mPsdu); + error = Request(true, SPINEL_CMD_PROP_VALUE_SET, SPINEL_PROP_STREAM_RAW, + SPINEL_DATATYPE_DATA_WLEN_S SPINEL_DATATYPE_UINT8_S SPINEL_DATATYPE_INT8_S, mTransmitFrame->mPsdu, + mTransmitFrame->mLength, mTransmitFrame->mChannel, mTransmitFrame->mInfo.mRxInfo.mRssi); + + if (error) + { + mState = OT_RADIO_STATE_RECEIVE; + +#if OPENTHREAD_ENABLE_DIAG + + if (otPlatDiagModeGet()) + { + otPlatDiagRadioTransmitDone(mInstance, mTransmitFrame, error); + } + else +#endif + { + otPlatRadioTxDone(mInstance, mTransmitFrame, NULL, error); + } + + mTxState = kIdle; + } + else + { + mTxState = kSent; + } +} + +otError RadioSpinel::WriteAll(const uint8_t *aBuffer, uint16_t aLength) +{ + otError error = OT_ERROR_NONE; + + while (aLength) + { + int rval = write(mSockFd, aBuffer, aLength); + + if (rval > 0) + { + aLength -= static_cast(rval); + aBuffer += static_cast(rval); + } + else if (rval < 0) + { + perror("send command failed"); + ExitNow(error = OT_ERROR_FAILED); + } + else + { + ExitNow(error = OT_ERROR_FAILED); + } + } + +exit: + return error; +} + +otError RadioSpinel::SendReset(void) +{ + otError error = OT_ERROR_NONE; + uint8_t buffer[kMaxSpinelFrame]; + UartTxBuffer txBuffer; + spinel_ssize_t packed; + + // Pack the header, command and key + packed = + spinel_datatype_pack(buffer, sizeof(buffer), "Ci", SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, SPINEL_CMD_RESET); + + VerifyOrExit(packed > 0 && static_cast(packed) <= sizeof(buffer), error = OT_ERROR_NO_BUFS); + + mHdlcEncoder.Init(txBuffer); + for (spinel_ssize_t i = 0; i < packed; i++) + { + error = mHdlcEncoder.Encode(buffer[i], txBuffer); + VerifyOrExit(error == OT_ERROR_NONE); + } + mHdlcEncoder.Finalize(txBuffer); + + error = WriteAll(txBuffer.GetBuffer(), txBuffer.GetLength()); + VerifyOrExit(error == OT_ERROR_NONE); + + sleep(0); + +exit: + return error; +} + +otError RadioSpinel::SendCommand(uint32_t aCommand, + spinel_prop_key_t aKey, + spinel_tid_t tid, + const char * aFormat, + va_list args) +{ + otError error = OT_ERROR_NONE; + uint8_t buffer[kMaxSpinelFrame]; + UartTxBuffer txBuffer; + spinel_ssize_t packed; + uint16_t offset; + + // Pack the header, command and key + packed = spinel_datatype_pack(buffer, sizeof(buffer), "Cii", SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0 | tid, + aCommand, aKey); + + VerifyOrExit(packed > 0 && static_cast(packed) <= sizeof(buffer), error = OT_ERROR_NO_BUFS); + + offset = static_cast(packed); + + // Pack the data (if any) + if (aFormat) + { + packed = spinel_datatype_vpack(buffer + offset, sizeof(buffer) - offset, aFormat, args); + VerifyOrExit(packed > 0 && static_cast(packed + offset) <= sizeof(buffer), error = OT_ERROR_NO_BUFS); + + offset += static_cast(packed); + } + + mHdlcEncoder.Init(txBuffer); + for (uint8_t i = 0; i < offset; i++) + { + error = mHdlcEncoder.Encode(buffer[i], txBuffer); + VerifyOrExit(error == OT_ERROR_NONE); + } + mHdlcEncoder.Finalize(txBuffer); + + error = WriteAll(txBuffer.GetBuffer(), txBuffer.GetLength()); + +exit: + return error; +} + +otError RadioSpinel::RequestV(bool aWait, uint32_t command, spinel_prop_key_t aKey, const char *aFormat, va_list aArgs) +{ + otError error = OT_ERROR_NONE; + spinel_tid_t tid = (aWait ? GetNextTid() : 0); + + VerifyOrExit(!aWait || tid > 0, error = OT_ERROR_BUSY); + + error = SendCommand(command, aKey, tid, aFormat, aArgs); + VerifyOrExit(error == OT_ERROR_NONE); + + if (aKey == SPINEL_PROP_STREAM_RAW) + { + // not allowed to send another frame before the last frame is done. + assert(mTxRadioTid == 0); + VerifyOrExit(mTxRadioTid == 0, error = OT_ERROR_BUSY); + mTxRadioTid = tid; + } + else if (aWait) + { + mWaitingKey = aKey; + mWaitingTid = tid; + error = WaitResponse(); + } + +exit: + return error; +} + +otError RadioSpinel::Request(bool aWait, uint32_t aCommand, spinel_prop_key_t aKey, const char *aFormat, ...) +{ + va_list args; + va_start(args, aFormat); + otError status = RequestV(aWait, aCommand, aKey, aFormat, args); + va_end(args); + return status; +} + +void RadioSpinel::HandleTransmitDone(uint32_t aCommand, + spinel_prop_key_t aKey, + const uint8_t * aBuffer, + uint16_t aLength) +{ + otError error = OT_ERROR_NONE; + spinel_status_t status = SPINEL_STATUS_OK; + spinel_ssize_t unpacked; + + VerifyOrExit(aCommand == SPINEL_CMD_PROP_VALUE_IS && aKey == SPINEL_PROP_LAST_STATUS, error = OT_ERROR_FAILED); + + unpacked = spinel_datatype_unpack(aBuffer, aLength, SPINEL_DATATYPE_UINT_PACKED_S, &status); + VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE); + + aBuffer += unpacked; + aLength -= static_cast(unpacked); + + if (status == SPINEL_STATUS_OK) + { + bool framePending = false; + unpacked = spinel_datatype_unpack(aBuffer, aLength, SPINEL_DATATYPE_BOOL_S, &framePending); + OT_UNUSED_VARIABLE(framePending); + VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE); + + aBuffer += unpacked; + aLength -= static_cast(unpacked); + + if (mAckWait) + { + VerifyOrExit(aLength > 0, error = OT_ERROR_FAILED); + SuccessOrExit(error = ParseRadioFrame(mRxRadioFrame, aBuffer, aLength)); + } + } + else + { + otLogWarnPlat(mInstance, "Spinel status: %d.", status); + error = SpinelStatusToOtError(status); + } + +exit: + mTxState = kDone; + mTxError = error; + LogIfFail(mInstance, "Handle transmit done failed", error); +} + +otError RadioSpinel::Transmit(otRadioFrame &aFrame) +{ + otError error = OT_ERROR_INVALID_STATE; + + VerifyOrExit(mState == OT_RADIO_STATE_RECEIVE); + mState = OT_RADIO_STATE_TRANSMIT; + error = OT_ERROR_NONE; + mTransmitFrame = &aFrame; + +exit: + return error; +} + +otError RadioSpinel::Receive(uint8_t aChannel) +{ + otError error = OT_ERROR_NONE; + + VerifyOrExit(mState != OT_RADIO_STATE_DISABLED, error = OT_ERROR_INVALID_STATE); + + if (mChannel != aChannel) + { + error = Set(SPINEL_PROP_PHY_CHAN, SPINEL_DATATYPE_UINT8_S, aChannel); + VerifyOrExit(error == OT_ERROR_NONE); + mChannel = aChannel; + } + + if (mState == OT_RADIO_STATE_SLEEP) + { + error = Set(SPINEL_PROP_MAC_RAW_STREAM_ENABLED, SPINEL_DATATYPE_BOOL_S, true); + VerifyOrExit(error == OT_ERROR_NONE); + } + + mTxState = kIdle; + mState = OT_RADIO_STATE_RECEIVE; + +exit: + assert(error == OT_ERROR_NONE); + return error; +} + +otError RadioSpinel::Sleep(void) +{ + otError error = OT_ERROR_NONE; + + switch (mState) + { + case OT_RADIO_STATE_RECEIVE: + error = sRadioSpinel.Set(SPINEL_PROP_MAC_RAW_STREAM_ENABLED, SPINEL_DATATYPE_BOOL_S, false); + VerifyOrExit(error == OT_ERROR_NONE); + + mState = OT_RADIO_STATE_SLEEP; + break; + + case OT_RADIO_STATE_SLEEP: + break; + + default: + error = OT_ERROR_INVALID_STATE; + break; + } + +exit: + return error; +} + +otError RadioSpinel::Enable(otInstance *aInstance) +{ + otError error = OT_ERROR_NONE; + + if (!otPlatRadioIsEnabled(mInstance)) + { + mInstance = aInstance; + + error = Set(SPINEL_PROP_PHY_ENABLED, SPINEL_DATATYPE_BOOL_S, true); + VerifyOrExit(error == OT_ERROR_NONE); + + error = Get(SPINEL_PROP_PHY_RX_SENSITIVITY, SPINEL_DATATYPE_INT8_S, &mRxSensitivity); + VerifyOrExit(error == OT_ERROR_NONE); + + mState = OT_RADIO_STATE_SLEEP; + } + +exit: + assert(error == OT_ERROR_NONE); + return error; +} + +otError RadioSpinel::Disable(void) +{ + otError error = OT_ERROR_NONE; + + if (otPlatRadioIsEnabled(mInstance)) + { + mInstance = NULL; + error = sRadioSpinel.Set(SPINEL_PROP_PHY_ENABLED, SPINEL_DATATYPE_BOOL_S, false); + VerifyOrExit(error == OT_ERROR_NONE); + + mState = OT_RADIO_STATE_DISABLED; + } + +exit: + return error; +} + +} // namespace ot + +void otPlatRadioGetIeeeEui64(otInstance *aInstance, uint8_t *aIeeeEui64) +{ + SuccessOrDie(sRadioSpinel.GetIeeeEui64(aIeeeEui64)); + OT_UNUSED_VARIABLE(aInstance); +} + +void otPlatRadioSetPanId(otInstance *aInstance, uint16_t panid) +{ + SuccessOrDie(sRadioSpinel.SetPanId(panid)); + OT_UNUSED_VARIABLE(aInstance); +} + +void otPlatRadioSetExtendedAddress(otInstance *aInstance, const otExtAddress *aAddress) +{ + otExtAddress addr; + + for (size_t i = 0; i < sizeof(addr); i++) + { + addr.m8[i] = aAddress->m8[sizeof(addr) - 1 - i]; + } + + SuccessOrDie(sRadioSpinel.SetExtendedAddress(addr)); + OT_UNUSED_VARIABLE(aInstance); +} + +void otPlatRadioSetShortAddress(otInstance *aInstance, uint16_t aAddress) +{ + sRadioSpinel.SetShortAddress(aAddress); + OT_UNUSED_VARIABLE(aInstance); +} + +void otPlatRadioSetPromiscuous(otInstance *aInstance, bool aEnable) +{ + sRadioSpinel.SetPromiscuous(aEnable); + OT_UNUSED_VARIABLE(aInstance); +} + +void platformRadioInit(const char *aRadioFile, const char *aRadioConfig) +{ + sRadioSpinel.Init(aRadioFile, aRadioConfig); +} + +void platformRadioDeinit(void) +{ + sRadioSpinel.Deinit(); +} + +bool otPlatRadioIsEnabled(otInstance *aInstance) +{ + OT_UNUSED_VARIABLE(aInstance); + return sRadioSpinel.IsEnabled(); +} + +otError otPlatRadioEnable(otInstance *aInstance) +{ + return sRadioSpinel.Enable(aInstance); +} + +otError otPlatRadioDisable(otInstance *aInstance) +{ + OT_UNUSED_VARIABLE(aInstance); + return sRadioSpinel.Disable(); +} + +otError otPlatRadioSleep(otInstance *aInstance) +{ + OT_UNUSED_VARIABLE(aInstance); + return sRadioSpinel.Sleep(); +} + +otError otPlatRadioReceive(otInstance *aInstance, uint8_t aChannel) +{ + OT_UNUSED_VARIABLE(aInstance); + return sRadioSpinel.Receive(aChannel); +} + +otError otPlatRadioTransmit(otInstance *aInstance, otRadioFrame *aFrame) +{ + OT_UNUSED_VARIABLE(aInstance); + return sRadioSpinel.Transmit(*aFrame); +} + +otRadioFrame *otPlatRadioGetTransmitBuffer(otInstance *aInstance) +{ + OT_UNUSED_VARIABLE(aInstance); + return &sRadioSpinel.GetTransmitFrame(); +} + +int8_t otPlatRadioGetRssi(otInstance *aInstance) +{ + OT_UNUSED_VARIABLE(aInstance); + return 0; +} + +otRadioCaps otPlatRadioGetCaps(otInstance *aInstance) +{ + OT_UNUSED_VARIABLE(aInstance); + return static_cast(OT_RADIO_CAPS_ACK_TIMEOUT | OT_RADIO_CAPS_TRANSMIT_RETRIES | + OT_RADIO_CAPS_CSMA_BACKOFF); +} + +bool otPlatRadioGetPromiscuous(otInstance *aInstance) +{ + OT_UNUSED_VARIABLE(aInstance); + return sRadioSpinel.GetPromiscuous(); +} + +void platformRadioUpdateFdSet(fd_set *aReadFdSet, fd_set *aWriteFdSet, int *aMaxFd, struct timeval *aTimeout) +{ + sRadioSpinel.UpdateFdSet(*aReadFdSet, *aWriteFdSet, *aMaxFd, *aTimeout); +} + +void platformRadioProcess(otInstance *aInstance, fd_set *aReadFdSet, fd_set *aWriteFdSet) +{ + sRadioSpinel.Process(*aReadFdSet, *aWriteFdSet); + OT_UNUSED_VARIABLE(aInstance); +} + +void otPlatRadioEnableSrcMatch(otInstance *aInstance, bool aEnable) +{ + SuccessOrDie(sRadioSpinel.EnableSrcMatch(aEnable)); + OT_UNUSED_VARIABLE(aInstance); +} + +otError otPlatRadioAddSrcMatchShortEntry(otInstance *aInstance, const uint16_t aShortAddress) +{ + OT_UNUSED_VARIABLE(aInstance); + return sRadioSpinel.AddSrcMatchShortEntry(aShortAddress); +} + +otError otPlatRadioAddSrcMatchExtEntry(otInstance *aInstance, const otExtAddress *aExtAddress) +{ + otExtAddress addr; + + for (size_t i = 0; i < sizeof(addr); i++) + { + addr.m8[i] = aExtAddress->m8[sizeof(addr) - 1 - i]; + } + + OT_UNUSED_VARIABLE(aInstance); + return sRadioSpinel.AddSrcMatchExtEntry(addr); +} + +otError otPlatRadioClearSrcMatchShortEntry(otInstance *aInstance, const uint16_t aShortAddress) +{ + OT_UNUSED_VARIABLE(aInstance); + return sRadioSpinel.ClearSrcMatchShortEntry(aShortAddress); +} + +otError otPlatRadioClearSrcMatchExtEntry(otInstance *aInstance, const otExtAddress *aExtAddress) +{ + otExtAddress addr; + + for (size_t i = 0; i < sizeof(addr); i++) + { + addr.m8[i] = aExtAddress->m8[sizeof(addr) - 1 - i]; + } + + OT_UNUSED_VARIABLE(aInstance); + return sRadioSpinel.ClearSrcMatchExtEntry(addr); +} + +void otPlatRadioClearSrcMatchShortEntries(otInstance *aInstance) +{ + SuccessOrDie(sRadioSpinel.ClearSrcMatchShortEntries()); + OT_UNUSED_VARIABLE(aInstance); +} + +void otPlatRadioClearSrcMatchExtEntries(otInstance *aInstance) +{ + SuccessOrDie(sRadioSpinel.ClearSrcMatchExtEntries()); + OT_UNUSED_VARIABLE(aInstance); +} + +otError otPlatRadioEnergyScan(otInstance *aInstance, uint8_t aScanChannel, uint16_t aScanDuration) +{ + OT_UNUSED_VARIABLE(aInstance); + OT_UNUSED_VARIABLE(aScanChannel); + OT_UNUSED_VARIABLE(aScanDuration); + + return OT_ERROR_NOT_IMPLEMENTED; +} + +otError otPlatRadioGetTransmitPower(otInstance *aInstance, int8_t *aPower) +{ + assert(aPower != NULL); + OT_UNUSED_VARIABLE(aInstance); + return sRadioSpinel.GetTransmitPower(*aPower); +} + +otError otPlatRadioSetTransmitPower(otInstance *aInstance, int8_t aPower) +{ + OT_UNUSED_VARIABLE(aInstance); + return sRadioSpinel.SetTransmitPower(aPower); +} + +int8_t otPlatRadioGetReceiveSensitivity(otInstance *aInstance) +{ + OT_UNUSED_VARIABLE(aInstance); + return sRadioSpinel.GetReceiveSensitivity(); +} diff --git a/src/posix/platform/radio_spinel.hpp b/src/posix/platform/radio_spinel.hpp new file mode 100644 index 000000000..81ea886dc --- /dev/null +++ b/src/posix/platform/radio_spinel.hpp @@ -0,0 +1,492 @@ +/* + * Copyright (c) 2018, 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 the spinel based radio transceiver. + */ + +#ifndef RADIO_SPINEL_HPP_ +#define RADIO_SPINEL_HPP_ + +#include "frame_queue.hpp" +#include "hdlc.hpp" +#include "spinel.h" + +namespace ot { + +class RadioSpinel +{ +public: + /** + * This constructor initializes the spinel based OpenThread transceiver. + * + */ + RadioSpinel(void); + + /** + * Initialize this radio transceiver. + * + * @param[in] aRadioFile The path to either a uart device or an executable. + * @param[in] aRadioConfig Parameters given to the device or executable. + * + */ + void Init(const char *aRadioFile, const char *aRadioConfig); + + /** + * Deinitialize this radio transceiver. + * + */ + void Deinit(void); + + /** + * This method gets the status of promiscuous mode. + * + * @retval true Promiscuous mode is enabled. + * @retval false Promiscuous mode is disabled. + * + */ + bool GetPromiscuous(void) const { return mPromiscuous; } + + /** + * This method sets the status of promiscuous mode. + * + * @param[in] aEnable Whether to enable or disable promiscuous mode. + * + * @retval OT_ERROR_NONE Succeeded. + * @retval OT_ERROR_BUSY Failed due to another operation is on going. + * @retval OT_ERROR_RESPONSE_TIMEOUT Failed due to no response received from the transceiver. + * + */ + otError SetPromiscuous(bool aEnable); + + /** + * This method sets the Short Address for address filtering. + * + * @param[in] aShortAddress The IEEE 802.15.4 Short Address. + * + * @retval OT_ERROR_NONE Succeeded. + * @retval OT_ERROR_BUSY Failed due to another operation is on going. + * @retval OT_ERROR_RESPONSE_TIMEOUT Failed due to no response received from the transceiver. + * + */ + otError SetShortAddress(uint16_t aAddress); + + /** + * This method gets the factory-assigned IEEE EUI-64 for this transceiver. + * + * @param[in] aInstance The OpenThread instance structure. + * @param[out] aIeeeEui64 A pointer to the factory-assigned IEEE EUI-64. + * + * @retval OT_ERROR_NONE Succeeded. + * @retval OT_ERROR_BUSY Failed due to another operation is on going. + * @retval OT_ERROR_RESPONSE_TIMEOUT Failed due to no response received from the transceiver. + * + */ + otError GetIeeeEui64(uint8_t *aIeeeEui64); + + /** + * This method sets the Extended Address for address filtering. + * + * @param[in] aExtAddress A pointer to the IEEE 802.15.4 Extended Address stored in little-endian byte order. + * + * @retval OT_ERROR_NONE Succeeded. + * @retval OT_ERROR_BUSY Failed due to another operation is on going. + * @retval OT_ERROR_RESPONSE_TIMEOUT Failed due to no response received from the transceiver. + * + */ + otError SetExtendedAddress(const otExtAddress &aAddress); + + /** + * This method sets the PAN ID for address filtering. + * + * @param[in] aPanId The IEEE 802.15.4 PAN ID. + * + * @retval OT_ERROR_NONE Succeeded. + * @retval OT_ERROR_BUSY Failed due to another operation is on going. + * @retval OT_ERROR_RESPONSE_TIMEOUT Failed due to no response received from the transceiver. + * + */ + otError SetPanId(uint16_t aPanId); + + /** + * This method gets the radio's transmit power in dBm. + * + * @param[out] aPower The transmit power in dBm. + * + * @retval OT_ERROR_NONE Succeeded. + * @retval OT_ERROR_BUSY Failed due to another operation is on going. + * @retval OT_ERROR_RESPONSE_TIMEOUT Failed due to no response received from the transceiver. + * + */ + otError GetTransmitPower(int8_t &aPower); + + /** + * This method sets the radio's transmit power in dBm. + * + * @param[in] aPower The transmit power in dBm. + * + * @retval OT_ERROR_NONE Succeeded. + * @retval OT_ERROR_BUSY Failed due to another operation is on going. + * @retval OT_ERROR_RESPONSE_TIMEOUT Failed due to no response received from the transceiver. + * + */ + otError SetTransmitPower(int8_t aPower); + + /** + * This method returns the radio receive sensitivity value. + * + * @returns The radio receive sensitivity value in dBm. + * + * @retval OT_ERROR_NONE Succeeded. + * @retval OT_ERROR_BUSY Failed due to another operation is on going. + * @retval OT_ERROR_RESPONSE_TIMEOUT Failed due to no response received from the transceiver. + * + */ + uint8_t GetReceiveSensitivity(void) const { return mRxSensitivity; } + + /** + * This method returns a reference to the transmit buffer. + * + * The caller forms the IEEE 802.15.4 frame in this buffer then calls otPlatRadioTransmit() to request transmission. + * + * @returns A reference to the transmit buffer. + * + */ + otRadioFrame &GetTransmitFrame(void) { return mTxRadioFrame; } + + /** + * This method enables or disables source address match feature. + * + * @param[in] aEnable Enable/disable source address match feature. + * + * @retval OT_ERROR_NONE Succeeded. + * @retval OT_ERROR_BUSY Failed due to another operation is on going. + * @retval OT_ERROR_RESPONSE_TIMEOUT Failed due to no response received from the transceiver. + * + */ + otError EnableSrcMatch(bool aEnable); + + /** + * This method adds a short address to the source address match table. + * + * @param[in] aInstance The OpenThread instance structure. + * @param[in] aShortAddress The short address to be added. + * + * @retval OT_ERROR_NONE Successfully added short address to the source match table. + * @retval OT_ERROR_BUSY Failed due to another operation is on going. + * @retval OT_ERROR_RESPONSE_TIMEOUT Failed due to no response received from the transceiver. + * @retval OT_ERROR_NO_BUFS No available entry in the source match table. + */ + otError AddSrcMatchShortEntry(const uint16_t aShortAddress); + + /** + * This method removes a short address from the source address match table. + * + * @param[in] aInstance The OpenThread instance structure. + * @param[in] aShortAddress The short address to be removed. + * + * @retval OT_ERROR_NONE Successfully removed short address from the source match table. + * @retval OT_ERROR_BUSY Failed due to another operation is on going. + * @retval OT_ERROR_RESPONSE_TIMEOUT Failed due to no response received from the transceiver. + * @retval OT_ERROR_NO_ADDRESS The short address is not in source address match table. + */ + otError ClearSrcMatchShortEntry(const uint16_t aShortAddress); + + /** + * Clear all short addresses from the source address match table. + * + * @param[in] aInstance The OpenThread instance structure. + * + * @retval OT_ERROR_NONE Succeeded. + * @retval OT_ERROR_BUSY Failed due to another operation is on going. + * @retval OT_ERROR_RESPONSE_TIMEOUT Failed due to no response received from the transceiver. + * + */ + otError ClearSrcMatchShortEntries(void); + + /** + * Add an extended address to the source address match table. + * + * @param[in] aInstance The OpenThread instance structure. + * @param[in] aExtAddress The extended address to be added stored in little-endian byte order. + * + * @retval OT_ERROR_NONE Successfully added extended address to the source match table. + * @retval OT_ERROR_BUSY Failed due to another operation is on going. + * @retval OT_ERROR_RESPONSE_TIMEOUT Failed due to no response received from the transceiver. + * @retval OT_ERROR_NO_BUFS No available entry in the source match table. + */ + otError AddSrcMatchExtEntry(const otExtAddress &aExtAddress); + + /** + * Remove an extended address from the source address match table. + * + * @param[in] aInstance The OpenThread instance structure. + * @param[in] aExtAddress The extended address to be removed stored in little-endian byte order. + * + * @retval OT_ERROR_NONE Successfully removed the extended address from the source match table. + * @retval OT_ERROR_BUSY Failed due to another operation is on going. + * @retval OT_ERROR_RESPONSE_TIMEOUT Failed due to no response received from the transceiver. + * @retval OT_ERROR_NO_ADDRESS The extended address is not in source address match table. + */ + otError ClearSrcMatchExtEntry(const otExtAddress &aExtAddress); + + /** + * Clear all the extended/long addresses from source address match table. + * + * @param[in] aInstance The OpenThread instance structure. + * + * @retval OT_ERROR_NONE Succeeded. + * @retval OT_ERROR_BUSY Failed due to another operation is on going. + * @retval OT_ERROR_RESPONSE_TIMEOUT Failed due to no response received from the transceiver. + * + */ + otError ClearSrcMatchExtEntries(void); + + /** + * This method switches the radio state from Receive to Transmit. + * + * @param[in] aFrame A reference to the transmitted frame. + * + * @retval OT_ERROR_NONE Successfully transitioned to Transmit. + * @retval OT_ERROR_BUSY Failed due to another transmission is on going. + * @retval OT_ERROR_RESPONSE_TIMEOUT Failed due to no response received from the transceiver. + * + * @retval OT_ERROR_INVALID_STATE The radio was not in the Receive state. + */ + otError Transmit(otRadioFrame &aFrame); + + /** + * This method swithes the radio state from Sleep to Receive. + * + * @param[in] aChannel The channel to use for receiving. + * + * @retval OT_ERROR_NONE Successfully transitioned to Receive. + * @retval OT_ERROR_INVALID_STATE The radio was disabled or transmitting. + * + */ + otError Receive(uint8_t aChannel); + + /** + * This method switches the radio state from Receive to Sleep. + * + * @retval OT_ERROR_NONE Successfully transitioned to Sleep. + * @retval OT_ERROR_BUSY The radio was transmitting + * @retval OT_ERROR_INVALID_STATE The radio was disabled + * + */ + otError Sleep(void); + + /** + * Enable the radio. + * + * @param[in] aInstance A pointer to the OpenThread instance. + * + * @retval OT_ERROR_NONE Successfully enabled. + * @retval OT_ERROR_FAILED The radio could not be enabled. + * + */ + otError Enable(otInstance *aInstance); + + /** + * Disable the radio. + * + * @retval OT_ERROR_NONE Successfully transitioned to Disabled. + * @retval OT_ERROR_BUSY Failed due to another operation is on going. + * @retval OT_ERROR_RESPONSE_TIMEOUT Failed due to no response received from the transceiver. + * + */ + otError Disable(void); + + /** + * This method checks whether radio is enabled or not. + * + * @returns TRUE if the radio is enabled, FALSE otherwise. + * + */ + bool IsEnabled(void) const { return mState != OT_RADIO_STATE_DISABLED; } + + /** + * This method updates the file descriptor sets with file descriptors used by the radio driver. + * + * @param[inout] aReadFdSet A reference to the read file descriptors. + * @param[inout] aWriteFdSet A reference to the write file descriptors. + * @param[inout] aMaxFd A reference to the max file descriptor. + * @param[inout] aTimeout A reference to the timeout. + * + */ + void UpdateFdSet(fd_set &aReadFdSet, fd_set &aWriteFdSet, int &aMaxFd, struct timeval &aTimeout); + + /** + * This method performs radio driver processing. + * + * @param[in] aReadFdSet A reference to the read file descriptors. + * @param[in] aWriteFdSet A reference to the write file descriptors. + * + */ + void Process(const fd_set &aReadFdSet, const fd_set &aWriteFdSet); + +private: + enum + { + kMaxSpinelFrame = 2048, ///< Max size in bytes for transfering spinel frames. + kMaxWaitTime = 2000, ///< Max time to wait for response in milliseconds. + }; + + void ReadAll(void); + otError WriteAll(const uint8_t *aBuffer, uint16_t aLength); + void ProcessFrameQueue(void); + + /** + * This method tries to retrieve a spinel property from OpenThread transceiver. + * + * @param[in] aKey Spinel property key. + * @param[in] aFormat Spinel formatter to unpack property value. + * @param[out] ... Variable arguments list. + * + * @retval OT_ERROR_NONE Successfully got the property. + * @retval OT_ERROR_BUSY Failed due to another operation is on going. + * @retval OT_ERROR_RESPONSE_TIMEOUT Failed due to no response received from the transceiver. + * + */ + otError Get(spinel_prop_key_t aKey, const char *aFormat, ...); + + /** + * This method tries to update a spinel property of OpenThread transceiver. + * + * @param[in] aKey Spinel property key. + * @param[in] aFormat Spinel formatter to pack property value. + * @param[in] ... Variable arguments list. + * + * @retval OT_ERROR_NONE Successfully set the property. + * @retval OT_ERROR_BUSY Failed due to another operation is on going. + * @retval OT_ERROR_RESPONSE_TIMEOUT Failed due to no response received from the transceiver. + * + */ + otError Set(spinel_prop_key_t aKey, const char *aFormat, ...); + + /** + * This method tries to insert a item into a spinel list property of OpenThread transceiver. + * + * @param[in] aKey Spinel property key. + * @param[in] aFormat Spinel formatter to pack the item. + * @param[in] ... Variable arguments list. + * + * @retval OT_ERROR_NONE Successfully insert item into the property. + * @retval OT_ERROR_BUSY Failed due to another operation is on going. + * @retval OT_ERROR_RESPONSE_TIMEOUT Failed due to no response received from the transceiver. + * + */ + otError Insert(spinel_prop_key_t aKey, const char *aFormat, ...); + + /** + * This method tries to remove a item from a spinel list property of OpenThread transceiver. + * + * @param[in] aKey Spinel property key. + * @param[in] aFormat Spinel formatter to pack the item. + * @param[in] ... Variable arguments list. + * + * @retval OT_ERROR_NONE Successfully removed item from the property. + * @retval OT_ERROR_BUSY Failed due to another operation is on going. + * @retval OT_ERROR_RESPONSE_TIMEOUT Failed due to no response received from the transceiver. + * + */ + otError Remove(spinel_prop_key_t aKey, const char *aFormat, ...); + + spinel_tid_t GetNextTid(void); + void FreeTid(spinel_tid_t tid) { mCmdTidsInUse &= ~(1 << tid); } + + otError RequestV(bool aWait, uint32_t aCommand, spinel_prop_key_t aKey, const char *aFormat, va_list aArgs); + otError Request(bool aWait, uint32_t aCommand, spinel_prop_key_t aKey, const char *aFormat, ...); + otError WaitResponse(void); + otError SendReset(void); + otError SendCommand(uint32_t command, + spinel_prop_key_t key, + spinel_tid_t tid, + const char * pack_format, + va_list args); + otError ParseRadioFrame(otRadioFrame &aFrame, const uint8_t *aBuffer, uint16_t aLength); + + static void HandleHdlcError(void *aContext, otError aError, uint8_t *aBuffer, uint16_t aLength); + static void HandleSpinelFrame(void *aContext, uint8_t *aBuffer, uint16_t aLength) + { + static_cast(aContext)->HandleSpinelFrame(aBuffer, aLength); + } + void HandleSpinelFrame(const uint8_t *aBuffer, uint16_t aLength); + + void HandleNotification(const uint8_t *aBuffer, uint16_t aLength); + void HandleValueIs(spinel_prop_key_t aKey, const uint8_t *aBuffer, uint16_t aLength); + + void HandleResponse(const uint8_t *aBuffer, uint16_t aLength); + void HandleTransmitDone(uint32_t aCommand, spinel_prop_key_t aKey, const uint8_t *aBuffer, uint16_t aLength); + void HandleWaitingResponse(uint32_t aCommand, spinel_prop_key_t aKey, const uint8_t *aBuffer, uint16_t aLength); + + void RadioReceive(void); + void RadioTransmit(void); + + otInstance *mInstance; + + uint16_t mCmdTidsInUse; ///< Used transaction ids. + spinel_tid_t mCmdNextTid; ///< Next available transaction id. + spinel_tid_t mTxRadioTid; ///< The transaction id used to send a radio frame. + spinel_tid_t mWaitingTid; ///< The transaction id of current transaction. + spinel_prop_key_t mWaitingKey; ///< The property key of current transaction. + const char * mPropertyFormat; ///< The spinel property format of current transaction. + va_list mPropertyArgs; ///< The arguments pack or unpack spinel property of current transcation. + uint32_t mExpectedCommand; ///< Expected response command of current transaction. + otError mError; ///< The result of current transaction. + + uint8_t mHdlcBuffer[kMaxSpinelFrame]; + Hdlc::Decoder mHdlcDecoder; + Hdlc::Encoder mHdlcEncoder; + FrameQueue mFrameQueue; + + uint8_t mRxPsdu[OT_RADIO_FRAME_MAX_SIZE]; + uint8_t mTxPsdu[OT_RADIO_FRAME_MAX_SIZE]; + otRadioFrame mRxRadioFrame; + otRadioFrame mTxRadioFrame; + otRadioFrame *mTransmitFrame; ///< Points to the frame to send + + otExtAddress mExtendedAddress; + uint16_t mShortAddress; + uint16_t mPanid; + uint8_t mChannel; + int8_t mRxSensitivity; + uint8_t mTxState; + otError mTxError; + + int mSockFd; + otRadioState mState; + bool mAckWait; + bool mPromiscuous; + bool mIsReady; +}; + +} // namespace ot + +#endif // RADIO_SPINEL_HPP_ diff --git a/src/posix/platform/random.c b/src/posix/platform/random.c new file mode 100644 index 000000000..37c132dcc --- /dev/null +++ b/src/posix/platform/random.c @@ -0,0 +1,132 @@ +/* + * 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 a pseudo-random number generator. + * + */ + +#include +#include + +#include "platform-posix.h" + +#include +#include + +#include "code_utils.h" + +static uint32_t sState = 1; + +void platformRandomInit(void) +{ +#if __SANITIZE_ADDRESS__ == 0 + + otError error; + + error = otPlatRandomGetTrue((uint8_t *)&sState, sizeof(sState)); + assert(error == OT_ERROR_NONE); + +#else // __SANITIZE_ADDRESS__ + + // Multiplying NODE_ID assures that no two nodes gets the same seed within an hour. + sState = (uint32_t)time(NULL) + (3600 * NODE_ID); + +#endif // __SANITIZE_ADDRESS__ +} + +uint32_t otPlatRandomGet(void) +{ + uint32_t mlcg, p, q; + uint64_t tmpstate; + + tmpstate = (uint64_t)33614 * (uint64_t)sState; + q = tmpstate & 0xffffffff; + q = q >> 1; + p = tmpstate >> 32; + mlcg = p + q; + + if (mlcg & 0x80000000) + { + mlcg &= 0x7fffffff; + mlcg++; + } + + sState = mlcg; + + return mlcg; +} + +otError otPlatRandomGetTrue(uint8_t *aOutput, uint16_t aOutputLength) +{ + otError error = OT_ERROR_NONE; + +#if __SANITIZE_ADDRESS__ == 0 + + FILE * file = NULL; + size_t readLength; + + otEXPECT_ACTION(aOutput && aOutputLength, error = OT_ERROR_INVALID_ARGS); + + file = fopen("/dev/urandom", "rb"); + otEXPECT_ACTION(file != NULL, error = OT_ERROR_FAILED); + + readLength = fread(aOutput, 1, aOutputLength, file); + otEXPECT_ACTION(readLength == aOutputLength, error = OT_ERROR_FAILED); + +exit: + + if (file != NULL) + { + fclose(file); + } + +#else // __SANITIZE_ADDRESS__ + + /* + * THE IMPLEMENTATION BELOW IS NOT COMPLIANT WITH THE THREAD SPECIFICATION. + * + * Address Sanitizer triggers test failures when reading random + * values from /dev/urandom. The pseudo-random number generator + * implementation below is only used to enable continuous + * integration checks with Address Sanitizer enabled. + */ + otEXPECT_ACTION(aOutput && aOutputLength, error = OT_ERROR_INVALID_ARGS); + + for (uint16_t length = 0; length < aOutputLength; length++) + { + aOutput[length] = (uint8_t)otPlatRandomGet(); + } + +exit: + +#endif // __SANITIZE_ADDRESS__ + + return error; +} diff --git a/src/posix/platform/settings.cpp b/src/posix/platform/settings.cpp new file mode 100644 index 000000000..9f9ee8105 --- /dev/null +++ b/src/posix/platform/settings.cpp @@ -0,0 +1,461 @@ +/* + * 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 non-volatile storage of settings. + * + */ + +#include +#include +#include +#include "utils/wrap_string.h" + +#include + +#include "openthread/platform/settings.h" +#include "openthread/types.h" + +#include + +#include "flash.h" + +#ifdef __cplusplus +extern "C" { +#endif + +enum +{ + kBlockAddBeginFlag = 0x1, + kBlockAddCompleteFlag = 0x02, + kBlockDeleteFlag = 0x04, + kBlockIndex0Flag = 0x08, +}; + +enum +{ + kSettingsFlagSize = 4, + kSettingsBlockDataSize = 255, + + kSettingsInSwap = 0xbe5cc5ef, + kSettingsInUse = 0xbe5cc5ee, + kSettingsNotUse = 0xbe5cc5ec, +}; + +OT_TOOL_PACKED_BEGIN +struct settingsBlock +{ + uint16_t key; + uint16_t flag; + uint16_t length; + uint16_t reserved; +} OT_TOOL_PACKED_END; + +/** + * @def SETTINGS_CONFIG_BASE_ADDRESS + * + * The base address of settings. + * + */ +#ifndef SETTINGS_CONFIG_BASE_ADDRESS +#define SETTINGS_CONFIG_BASE_ADDRESS 0x39000 +#endif // SETTINGS_CONFIG_BASE_ADDRESS + +/** + * @def SETTINGS_CONFIG_PAGE_SIZE + * + * The page size of settings. + * + */ +#ifndef SETTINGS_CONFIG_PAGE_SIZE +#define SETTINGS_CONFIG_PAGE_SIZE 0x800 +#endif // SETTINGS_CONFIG_PAGE_SIZE + +/** + * @def SETTINGS_CONFIG_PAGE_NUM + * + * The page number of settings. + * + */ +#ifndef SETTINGS_CONFIG_PAGE_NUM +#define SETTINGS_CONFIG_PAGE_NUM 2 +#endif // SETTINGS_CONFIG_PAGE_NUM + +static uint32_t sSettingsBaseAddress; +static uint32_t sSettingsUsedSize; + +static uint16_t getAlignLength(uint16_t length) +{ + return (length + 3) & 0xfffc; +} + +static void setSettingsFlag(uint32_t aBase, uint32_t aFlag) +{ + utilsFlashWrite(aBase, reinterpret_cast(&aFlag), sizeof(aFlag)); +} + +static void initSettings(uint32_t aBase, uint32_t aFlag) +{ + uint32_t address = aBase; + uint32_t settingsSize = SETTINGS_CONFIG_PAGE_NUM > 1 ? SETTINGS_CONFIG_PAGE_SIZE * SETTINGS_CONFIG_PAGE_NUM / 2 + : SETTINGS_CONFIG_PAGE_SIZE; + + while (address < (aBase + settingsSize)) + { + utilsFlashErasePage(address); + utilsFlashStatusWait(1000); + address += SETTINGS_CONFIG_PAGE_SIZE; + } + + setSettingsFlag(aBase, aFlag); +} + +static uint32_t swapSettingsBlock(otInstance *aInstance) +{ + uint32_t oldBase = sSettingsBaseAddress; + uint32_t swapAddress = oldBase; + uint32_t usedSize = sSettingsUsedSize; + uint8_t pageNum = SETTINGS_CONFIG_PAGE_NUM; + uint32_t settingsSize = pageNum > 1 ? SETTINGS_CONFIG_PAGE_SIZE * pageNum / 2 : SETTINGS_CONFIG_PAGE_SIZE; + + (void)aInstance; + + otEXPECT(pageNum > 1); + + sSettingsBaseAddress = + (swapAddress == SETTINGS_CONFIG_BASE_ADDRESS) ? (swapAddress + settingsSize) : SETTINGS_CONFIG_BASE_ADDRESS; + + initSettings(sSettingsBaseAddress, static_cast(kSettingsInSwap)); + sSettingsUsedSize = kSettingsFlagSize; + swapAddress += kSettingsFlagSize; + + while (swapAddress < (oldBase + usedSize)) + { + OT_TOOL_PACKED_BEGIN + struct addSettingsBlock + { + struct settingsBlock block; + uint8_t data[kSettingsBlockDataSize]; + } OT_TOOL_PACKED_END addBlock; + bool valid = true; + + utilsFlashRead(swapAddress, reinterpret_cast(&addBlock.block), sizeof(struct settingsBlock)); + swapAddress += sizeof(struct settingsBlock); + + if (!(addBlock.block.flag & kBlockAddCompleteFlag) && (addBlock.block.flag & kBlockDeleteFlag)) + { + uint32_t address = swapAddress + getAlignLength(addBlock.block.length); + + while (address < (oldBase + usedSize)) + { + struct settingsBlock block; + + utilsFlashRead(address, reinterpret_cast(&block), sizeof(block)); + + if (!(block.flag & kBlockAddCompleteFlag) && (block.flag & kBlockDeleteFlag) && + !(block.flag & kBlockIndex0Flag) && (block.key == addBlock.block.key)) + { + valid = false; + break; + } + + address += (getAlignLength(block.length) + sizeof(struct settingsBlock)); + } + + if (valid) + { + utilsFlashRead(swapAddress, addBlock.data, getAlignLength(addBlock.block.length)); + utilsFlashWrite(sSettingsBaseAddress + sSettingsUsedSize, reinterpret_cast(&addBlock), + getAlignLength(addBlock.block.length) + sizeof(struct settingsBlock)); + sSettingsUsedSize += (sizeof(struct settingsBlock) + getAlignLength(addBlock.block.length)); + } + } + else if (addBlock.block.flag == 0xff) + { + break; + } + + swapAddress += getAlignLength(addBlock.block.length); + } + + setSettingsFlag(sSettingsBaseAddress, static_cast(kSettingsInUse)); + setSettingsFlag(oldBase, static_cast(kSettingsNotUse)); + +exit: + return settingsSize - sSettingsUsedSize; +} + +static otError addSetting(otInstance * aInstance, + uint16_t aKey, + bool aIndex0, + const uint8_t *aValue, + uint16_t aValueLength) +{ + otError error = OT_ERROR_NONE; + OT_TOOL_PACKED_BEGIN + struct addSettingsBlock + { + struct settingsBlock block; + uint8_t data[kSettingsBlockDataSize]; + } OT_TOOL_PACKED_END addBlock; + uint32_t settingsSize = SETTINGS_CONFIG_PAGE_NUM > 1 ? SETTINGS_CONFIG_PAGE_SIZE * SETTINGS_CONFIG_PAGE_NUM / 2 + : SETTINGS_CONFIG_PAGE_SIZE; + + addBlock.block.flag = 0xff; + addBlock.block.key = aKey; + + if (aIndex0) + { + addBlock.block.flag &= (~kBlockIndex0Flag); + } + + addBlock.block.flag &= (~kBlockAddBeginFlag); + addBlock.block.length = aValueLength; + + if ((sSettingsUsedSize + getAlignLength(addBlock.block.length) + sizeof(struct settingsBlock)) >= settingsSize) + { + otEXPECT_ACTION(swapSettingsBlock(aInstance) >= + (getAlignLength(addBlock.block.length) + sizeof(struct settingsBlock)), + error = OT_ERROR_NO_BUFS); + } + + utilsFlashWrite(sSettingsBaseAddress + sSettingsUsedSize, reinterpret_cast(&addBlock.block), + sizeof(struct settingsBlock)); + + memset(addBlock.data, 0xff, kSettingsBlockDataSize); + memcpy(addBlock.data, aValue, addBlock.block.length); + + utilsFlashWrite(sSettingsBaseAddress + sSettingsUsedSize + sizeof(struct settingsBlock), + reinterpret_cast(addBlock.data), getAlignLength(addBlock.block.length)); + + addBlock.block.flag &= (~kBlockAddCompleteFlag); + utilsFlashWrite(sSettingsBaseAddress + sSettingsUsedSize, reinterpret_cast(&addBlock.block), + sizeof(struct settingsBlock)); + sSettingsUsedSize += (sizeof(struct settingsBlock) + getAlignLength(addBlock.block.length)); + +exit: + return error; +} + +// settings API +void otPlatSettingsInit(otInstance *aInstance) +{ + uint8_t index; + uint32_t settingsSize = SETTINGS_CONFIG_PAGE_NUM > 1 ? SETTINGS_CONFIG_PAGE_SIZE * SETTINGS_CONFIG_PAGE_NUM / 2 + : SETTINGS_CONFIG_PAGE_SIZE; + + (void)aInstance; + + sSettingsBaseAddress = SETTINGS_CONFIG_BASE_ADDRESS; + + utilsFlashInit(); + + for (index = 0; index < 2; index++) + { + uint32_t blockFlag; + + sSettingsBaseAddress += settingsSize * index; + utilsFlashRead(sSettingsBaseAddress, reinterpret_cast(&blockFlag), sizeof(blockFlag)); + + if (blockFlag == kSettingsInUse) + { + break; + } + } + + if (index == 2) + { + initSettings(sSettingsBaseAddress, static_cast(kSettingsInUse)); + } + + sSettingsUsedSize = kSettingsFlagSize; + + while (sSettingsUsedSize < settingsSize) + { + struct settingsBlock block; + + utilsFlashRead(sSettingsBaseAddress + sSettingsUsedSize, reinterpret_cast(&block), sizeof(block)); + + if (!(block.flag & kBlockAddBeginFlag)) + { + sSettingsUsedSize += (getAlignLength(block.length) + sizeof(struct settingsBlock)); + } + else + { + break; + } + } +} + +otError otPlatSettingsBeginChange(otInstance *aInstance) +{ + (void)aInstance; + return OT_ERROR_NONE; +} + +otError otPlatSettingsCommitChange(otInstance *aInstance) +{ + (void)aInstance; + return OT_ERROR_NONE; +} + +otError otPlatSettingsAbandonChange(otInstance *aInstance) +{ + (void)aInstance; + return OT_ERROR_NONE; +} + +otError otPlatSettingsGet(otInstance *aInstance, uint16_t aKey, int aIndex, uint8_t *aValue, uint16_t *aValueLength) +{ + otError error = OT_ERROR_NOT_FOUND; + uint32_t address = sSettingsBaseAddress + kSettingsFlagSize; + uint16_t valueLength = 0; + int index = 0; + + (void)aInstance; + + while (address < (sSettingsBaseAddress + sSettingsUsedSize)) + { + struct settingsBlock block; + + utilsFlashRead(address, reinterpret_cast(&block), sizeof(block)); + + if (block.key == aKey) + { + if (!(block.flag & kBlockIndex0Flag)) + { + index = 0; + } + + if (!(block.flag & kBlockAddCompleteFlag) && (block.flag & kBlockDeleteFlag)) + { + if (index == aIndex) + { + uint16_t readLength = block.length; + + // only perform read if an input buffer was passed in + if (aValue != NULL && aValueLength != NULL) + { + // adjust read length if input buffer length is smaller + if (readLength > *aValueLength) + { + readLength = *aValueLength; + } + + utilsFlashRead(address + sizeof(struct settingsBlock), aValue, readLength); + } + + valueLength = block.length; + error = OT_ERROR_NONE; + } + + index++; + } + } + + address += (getAlignLength(block.length) + sizeof(struct settingsBlock)); + } + + if (aValueLength != NULL) + { + *aValueLength = valueLength; + } + + return error; +} + +otError otPlatSettingsSet(otInstance *aInstance, uint16_t aKey, const uint8_t *aValue, uint16_t aValueLength) +{ + return addSetting(aInstance, aKey, true, aValue, aValueLength); +} + +otError otPlatSettingsAdd(otInstance *aInstance, uint16_t aKey, const uint8_t *aValue, uint16_t aValueLength) +{ + uint16_t length; + bool index0; + + index0 = (otPlatSettingsGet(aInstance, aKey, 0, NULL, &length) == OT_ERROR_NOT_FOUND ? true : false); + return addSetting(aInstance, aKey, index0, aValue, aValueLength); +} + +otError otPlatSettingsDelete(otInstance *aInstance, uint16_t aKey, int aIndex) +{ + otError error = OT_ERROR_NOT_FOUND; + uint32_t address = sSettingsBaseAddress + kSettingsFlagSize; + int index = 0; + + (void)aInstance; + + while (address < (sSettingsBaseAddress + sSettingsUsedSize)) + { + struct settingsBlock block; + + utilsFlashRead(address, reinterpret_cast(&block), sizeof(block)); + + if (block.key == aKey) + { + if (!(block.flag & kBlockIndex0Flag)) + { + index = 0; + } + + if (!(block.flag & kBlockAddCompleteFlag) && (block.flag & kBlockDeleteFlag)) + { + if (aIndex == index || aIndex == -1) + { + error = OT_ERROR_NONE; + block.flag &= (~kBlockDeleteFlag); + utilsFlashWrite(address, reinterpret_cast(&block), sizeof(block)); + } + + if (index == 1 && aIndex == 0) + { + block.flag &= (~kBlockIndex0Flag); + utilsFlashWrite(address, reinterpret_cast(&block), sizeof(block)); + } + + index++; + } + } + + address += (getAlignLength(block.length) + sizeof(struct settingsBlock)); + } + + return error; +} + +void otPlatSettingsWipe(otInstance *aInstance) +{ + initSettings(sSettingsBaseAddress, static_cast(kSettingsInUse)); + otPlatSettingsInit(aInstance); +} + +#ifdef __cplusplus +}; +#endif diff --git a/src/posix/platform/spi-stubs.c b/src/posix/platform/spi-stubs.c new file mode 100644 index 000000000..a56184bbc --- /dev/null +++ b/src/posix/platform/spi-stubs.c @@ -0,0 +1,93 @@ +/* + * 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 spi stubs. + * + */ + +#include "platform-posix.h" + +#include +#include + +#include +#include +#include + +#if OPENTHREAD_ENABLE_NCP_SPI + +// Spi-slave stubs + +otError otPlatSpiSlaveEnable(otPlatSpiSlaveTransactionCompleteCallback aCompleteCallback, + otPlatSpiSlaveTransactionProcessCallback aProcessCallback, + void * aContext) +{ + (void)aCompleteCallback; + (void)aProcessCallback; + (void)aContext; + + fprintf(stderr, "\nNo SPI support for posix platform."); + exit(0); + + return OT_ERROR_NOT_IMPLEMENTED; +} + +void otPlatSpiSlaveDisable(void) +{ +} + +otError otPlatSpiSlavePrepareTransaction(uint8_t *aOutputBuf, + uint16_t aOutputBufLen, + uint8_t *aInputBuf, + uint16_t aInputBufLen, + bool aRequestTransactionFlag) +{ + (void)aOutputBuf; + (void)aOutputBufLen; + (void)aInputBuf; + (void)aInputBufLen; + (void)aRequestTransactionFlag; + + return OT_ERROR_NOT_IMPLEMENTED; +} + +// Uart + +void otPlatUartSendDone(void) +{ +} + +void otPlatUartReceived(const uint8_t *aBuf, uint16_t aBufLength) +{ + (void)aBuf; + (void)aBufLength; +} + +#endif // OPENTHREAD_ENABLE_NCP_SPI diff --git a/src/posix/platform/spinel.c b/src/posix/platform/spinel.c new file mode 120000 index 000000000..a764fdc82 --- /dev/null +++ b/src/posix/platform/spinel.c @@ -0,0 +1 @@ +../../ncp/spinel.c \ No newline at end of file diff --git a/src/posix/platform/uart.c b/src/posix/platform/uart.c new file mode 100644 index 000000000..6e86e8fbe --- /dev/null +++ b/src/posix/platform/uart.c @@ -0,0 +1,314 @@ +/* + * 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 "platform-posix.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "code_utils.h" + +int posix_openpt(int oflag); +int grantpt(int fildes); +int unlockpt(int fd); +char *ptsname(int fd); + +static uint8_t s_receive_buffer[128]; +static const uint8_t *s_write_buffer; +static uint16_t s_write_length; +static int s_in_fd; +static int s_out_fd; + +static struct termios original_stdin_termios; +static struct termios original_stdout_termios; + +static void restore_stdin_termios(void) +{ + tcsetattr(s_in_fd, TCSAFLUSH, &original_stdin_termios); +} + +static void restore_stdout_termios(void) +{ + tcsetattr(s_out_fd, TCSAFLUSH, &original_stdout_termios); +} + +void platformUartRestore(void) +{ + restore_stdin_termios(); + restore_stdout_termios(); + dup2(s_out_fd, STDOUT_FILENO); +} + +otError otPlatUartEnable(void) +{ + otError error = OT_ERROR_NONE; + struct termios termios; + +#ifdef OPENTHREAD_TARGET_LINUX + // Ensure we terminate this process if the + // parent process dies. + prctl(PR_SET_PDEATHSIG, SIGHUP); +#endif + + s_in_fd = dup(STDIN_FILENO); + s_out_fd = dup(STDOUT_FILENO); + dup2(STDERR_FILENO, STDOUT_FILENO); + + // We need this signal to make sure that this + // process terminates properly. + signal(SIGPIPE, SIG_DFL); + + if (isatty(s_in_fd)) + { + tcgetattr(s_in_fd, &original_stdin_termios); + atexit(&restore_stdin_termios); + } + + if (isatty(s_out_fd)) + { + tcgetattr(s_out_fd, &original_stdout_termios); + atexit(&restore_stdout_termios); + } + + if (isatty(s_in_fd)) + { + // get current configuration + otEXPECT_ACTION(tcgetattr(s_in_fd, &termios) == 0, perror("tcgetattr"); error = OT_ERROR_GENERIC); + + // Set up the termios settings for raw mode. This turns + // off input/output processing, line processing, and character processing. + cfmakeraw(&termios); + + // Set up our cflags for local use. Turn on hangup-on-close. + termios.c_cflag |= HUPCL | CREAD | CLOCAL; + + // "Minimum number of characters for noncanonical read" + termios.c_cc[VMIN] = 1; + + // "Timeout in deciseconds for noncanonical read" + termios.c_cc[VTIME] = 0; + + // configure baud rate + otEXPECT_ACTION(cfsetispeed(&termios, B115200) == 0, perror("cfsetispeed"); error = OT_ERROR_GENERIC); + + // set configuration + otEXPECT_ACTION(tcsetattr(s_in_fd, TCSANOW, &termios) == 0, perror("tcsetattr"); error = OT_ERROR_GENERIC); + } + + if (isatty(s_out_fd)) + { + // get current configuration + otEXPECT_ACTION(tcgetattr(s_out_fd, &termios) == 0, perror("tcgetattr"); error = OT_ERROR_GENERIC); + + // Set up the termios settings for raw mode. This turns + // off input/output processing, line processing, and character processing. + cfmakeraw(&termios); + + // Absolutely obliterate all output processing. + termios.c_oflag = 0; + + // Set up our cflags for local use. Turn on hangup-on-close. + termios.c_cflag |= HUPCL | CREAD | CLOCAL; + + // configure baud rate + otEXPECT_ACTION(cfsetospeed(&termios, B115200) == 0, perror("cfsetospeed"); error = OT_ERROR_GENERIC); + + // set configuration + otEXPECT_ACTION(tcsetattr(s_out_fd, TCSANOW, &termios) == 0, perror("tcsetattr"); error = OT_ERROR_GENERIC); + } + + return error; + +exit: + close(s_in_fd); + close(s_out_fd); + return error; +} + +otError otPlatUartDisable(void) +{ + otError error = OT_ERROR_NONE; + + close(s_in_fd); + close(s_out_fd); + + return error; +} + +otError otPlatUartSend(const uint8_t *aBuf, uint16_t aBufLength) +{ + otError error = OT_ERROR_NONE; + + otEXPECT_ACTION(s_write_length == 0, error = OT_ERROR_BUSY); + + s_write_buffer = aBuf; + s_write_length = aBufLength; + +exit: + return error; +} + +void platformUartUpdateFdSet(fd_set *aReadFdSet, fd_set *aWriteFdSet, fd_set *aErrorFdSet, int *aMaxFd) +{ + if (aReadFdSet != NULL) + { + FD_SET(s_in_fd, aReadFdSet); + + if (aErrorFdSet != NULL) + { + FD_SET(s_in_fd, aErrorFdSet); + } + + if (aMaxFd != NULL && *aMaxFd < s_in_fd) + { + *aMaxFd = s_in_fd; + } + } + + if ((aWriteFdSet != NULL) && (s_write_length > 0)) + { + FD_SET(s_out_fd, aWriteFdSet); + + if (aErrorFdSet != NULL) + { + FD_SET(s_out_fd, aErrorFdSet); + } + + if (aMaxFd != NULL && *aMaxFd < s_out_fd) + { + *aMaxFd = s_out_fd; + } + } +} + +void platformUartProcess(const fd_set *aReadFdSet, const fd_set *aWriteFdSet, const fd_set *aErrorFdSet) +{ + ssize_t rval; + errno = 0; + + if (FD_ISSET(s_in_fd, aErrorFdSet)) + { + perror("s_in_fd"); + exit(EXIT_FAILURE); + } + + if (FD_ISSET(s_out_fd, aErrorFdSet)) + { + perror("s_out_fd"); + exit(EXIT_FAILURE); + } + + if (FD_ISSET(s_in_fd, aReadFdSet)) + { + rval = read(s_in_fd, s_receive_buffer, sizeof(s_receive_buffer)); + + if (rval < 0) + { + perror("read"); + exit(EXIT_FAILURE); + } + else if (rval > 0) + { + otPlatUartReceived(s_receive_buffer, (uint16_t)rval); + } + } + + if ((s_write_length > 0) && (FD_ISSET(s_out_fd, aWriteFdSet))) + { + rval = write(s_out_fd, s_write_buffer, s_write_length); + + if (rval <= 0) + { + perror("write"); + exit(EXIT_FAILURE); + } + + s_write_buffer += (uint16_t)rval; + s_write_length -= (uint16_t)rval; + + if (s_write_length == 0) + { + otPlatUartSendDone(); + } + } +} + +#if OPENTHREAD_CONFIG_ENABLE_DEBUG_UART && (OPENTHREAD_CONFIG_LOG_OUTPUT == OPENTHREAD_CONFIG_LOG_OUTPUT_DEBUG_UART) + +static FILE *posix_logfile; + +otError otPlatDebugUart_logfile(const char *filename) +{ + posix_logfile = fopen(filename, "wt"); + + return posix_logfile ? OT_ERROR_NONE : OT_ERROR_FAILED; +} + +void otPlatDebugUart_putchar_raw(int c) +{ + FILE *fp; + + /* note: log file will have a mix of cr/lf and + * in some/many cases duplicate cr because in + * some cases the log function {ie: Mbed} already + * includes the CR or LF... but other log functions + * do not include cr/lf and expect it appened + */ + fp = posix_logfile; + + if (fp != NULL) + { + /* log is lost ... until a file is setup */ + fputc(c, fp); + /* we could "fflush" but will not */ + } +} + +int otPlatDebugUart_kbhit(void) +{ + /* not supported */ + return 0; +} + +int otPlatDebugUart_getc(void) +{ + /* not supported */ + return -1; +} + +#endif diff --git a/tests/Makefile.am b/tests/Makefile.am index f00547a2a..dc4d60170 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -48,7 +48,7 @@ PRETTY_SUBDIRS = \ unit \ $(NULL) -if OPENTHREAD_EXAMPLES_POSIX +if OPENTHREAD_POSIX if OPENTHREAD_ENABLE_CLI SUBDIRS += \ unit \ diff --git a/tests/scripts/Makefile.am b/tests/scripts/Makefile.am index b7fc379ea..4613baba5 100644 --- a/tests/scripts/Makefile.am +++ b/tests/scripts/Makefile.am @@ -39,7 +39,7 @@ DIST_SUBDIRS = \ SUBDIRS = \ $(NULL) -if OPENTHREAD_EXAMPLES_POSIX +if OPENTHREAD_POSIX if OPENTHREAD_ENABLE_CLI SUBDIRS += \ thread-cert \ diff --git a/tests/scripts/thread-cert/node_cli.py b/tests/scripts/thread-cert/node_cli.py index 1fd42ed9c..94538f3a5 100644 --- a/tests/scripts/thread-cert/node_cli.py +++ b/tests/scripts/thread-cert/node_cli.py @@ -59,13 +59,17 @@ class otCli: def __init_sim(self, nodeid, mode): """ Initialize a simulation node. """ - if "OT_CLI_PATH" in os.environ.keys(): + if 'OT_CLI_PATH' in os.environ.keys(): cmd = os.environ['OT_CLI_PATH'] - elif "top_builddir" in os.environ.keys(): + elif 'top_builddir' in os.environ.keys(): srcdir = os.environ['top_builddir'] cmd = '%s/examples/apps/cli/ot-cli-%s' % (srcdir, mode) else: cmd = './ot-cli-%s' % mode + + if 'RADIO_DEVICE' in os.environ: + cmd += ' %s' % os.environ['RADIO_DEVICE'] + cmd += ' %d' % nodeid print ("%s" % cmd) @@ -77,11 +81,18 @@ class otCli: def __init_ncp_sim(self, nodeid, mode): """ Initialize an NCP simulation node. """ - if "top_builddir" in os.environ.keys(): - builddir = os.environ['top_builddir'] - cmd = 'spinel-cli.py -p %s/examples/apps/ncp/ot-ncp-%s -n' % (builddir, mode) + if 'RADIO_DEVICE' in os.environ: + args = ' %s' % os.environ['RADIO_DEVICE'] else: - cmd = './ot-ncp-%s' % mode + args = '' + + if 'OT_NCP_PATH' in os.environ.keys(): + cmd = 'spinel-cli.py -p "%s%s" -n' % (os.environ['OT_NCP_PATH'], args) + elif "top_builddir" in os.environ.keys(): + builddir = os.environ['top_builddir'] + cmd = 'spinel-cli.py -p "%s/examples/apps/ncp/ot-ncp-%s%s" -n' % (builddir, mode, args) + else: + cmd = 'spinel-cli.py -p "./ot-ncp-%s%s" -n' % (mode, args) cmd += ' %d' % nodeid print ("%s" % cmd) @@ -106,6 +117,7 @@ class otCli: def send_command(self, cmd): print("%d: %s" % (self.nodeid, cmd)) self.pexpect.send(cmd + '\n') + sys.stdout.flush() if isinstance(self.simulator, simulator.VirtualTime): self.simulator.receive_events() diff --git a/tests/unit/Makefile.am b/tests/unit/Makefile.am index a9c66ba5d..2ec53a361 100644 --- a/tests/unit/Makefile.am +++ b/tests/unit/Makefile.am @@ -51,12 +51,24 @@ if OPENTHREAD_BUILD_TESTS AM_CPPFLAGS = \ -DOPENTHREAD_FTD=1 \ - -I$(top_srcdir)/examples/platforms \ -I$(top_srcdir)/include \ -I$(top_srcdir)/src \ -I$(top_srcdir)/src/core \ $(NULL) +if OPENTHREAD_EXAMPLES_POSIX +AM_CPPFLAGS += \ + -I$(top_srcdir)/examples/platforms \ + $(NULL) +endif + +if OPENTHREAD_ENABLE_POSIX_APP +AM_CPPFLAGS += \ + -DOPENTHREAD_ENABLE_POSIX_APP=1 \ + -I$(top_srcdir)/src/posix/platform \ + $(NULL) +endif + if OPENTHREAD_ENABLE_NCP COMMON_LDADD = \ @@ -80,6 +92,12 @@ COMMON_LDADD += \ $(NULL) endif # OPENTHREAD_ENABLE_BUILTIN_MBEDTLS +if OPENTHREAD_ENABLE_POSIX_APP +COMMON_LDADD += \ + -lutil + $(NULL) +endif + # Test applications that should be run when the 'check' target is run. check_PROGRAMS = \ @@ -210,10 +228,16 @@ test_toolchain_LDADD = $(COMMON_LDADD) test_toolchain_SOURCES = test_platform.cpp test_toolchain.cpp test_toolchain_c.c if OPENTHREAD_ENABLE_DIAG -test_diag_LDADD = $(top_builddir)/src/diag/libopenthread-diag.a \ - $(top_builddir)/examples/platforms/posix/libopenthread-posix.a \ - $(NULL) +test_diag_LDADD = $(top_builddir)/src/diag/libopenthread-diag.a test_diag_SOURCES = test_diag.cpp +if OPENTHREAD_ENABLE_POSIX_APP +test_diag_LDADD += $(top_builddir)/src/posix/platform/libopenthread-posix.a \ + -lutil \ + $(NULL) +endif +if OPENTHREAD_EXAMPLES_POSIX +test_diag_LDADD += $(top_builddir)/examples/platforms/posix/libopenthread-posix.a +endif endif PRETTY_FILES = \ diff --git a/tests/unit/test_diag.cpp b/tests/unit/test_diag.cpp index 01d0cfd2a..76549d194 100644 --- a/tests/unit/test_diag.cpp +++ b/tests/unit/test_diag.cpp @@ -29,6 +29,8 @@ #include #include +#include + #include "utils/wrap_string.h" #include "platform.h" @@ -227,9 +229,12 @@ void TestDiag(void) }}; // initialize platform layer - int argc = 2; - char *argv[8] = {(char *)"test_diag", (char *)"1"}; - PlatformInit(argc, argv); +#if OPENTHREAD_ENABLE_POSIX_APP + char *argv[] = {(char *)"test_diag", getenv("RADIO_DEVICE"), (char *)"1"}; +#else + char *argv[] = {(char *)"test_diag", (char *)"1"}; +#endif + PlatformInit(sizeof(argv) / sizeof(char *), argv); // initialize diagnostics module otDiagInit(NULL);