From 3078359e38c3b8f502197cc8429959a15ee8a18e Mon Sep 17 00:00:00 2001 From: erja-gp Date: Tue, 11 Jul 2017 18:10:00 +0200 Subject: [PATCH] [platforms] new platform Qorvo GP712. (#1942) --- configure.ac | 14 +- examples/Makefile-gp712 | 271 ++++++++++++++ examples/apps/cli/Makefile.am | 8 + examples/platforms/Makefile.am | 6 + examples/platforms/gp712/Makefile.am | 73 ++++ examples/platforms/gp712/README.md | 71 ++++ examples/platforms/gp712/alarm.c | 78 ++++ examples/platforms/gp712/alarm_qorvo.h | 92 +++++ examples/platforms/gp712/diag.c | 83 +++++ examples/platforms/gp712/flash.c | 158 ++++++++ examples/platforms/gp712/logging.c | 107 ++++++ examples/platforms/gp712/misc.c | 73 ++++ .../gp712/openthread-core-gp712-config.h | 46 +++ examples/platforms/gp712/platform.c | 93 +++++ examples/platforms/gp712/platform_qorvo.h | 77 ++++ examples/platforms/gp712/radio.c | 312 ++++++++++++++++ examples/platforms/gp712/radio_qorvo.h | 202 +++++++++++ examples/platforms/gp712/random.c | 130 +++++++ examples/platforms/gp712/random_qorvo.h | 57 +++ examples/platforms/gp712/uart-posix.c | 275 ++++++++++++++ examples/platforms/gp712/uart-socket.c | 337 ++++++++++++++++++ examples/platforms/gp712/uart_qorvo.h | 89 +++++ third_party/Qorvo/LICENSE.txt | 21 ++ third_party/Qorvo/README.md | 13 + third_party/Qorvo/gp712/libQorvoRPi.a | Bin 0 -> 316682 bytes 25 files changed, 2683 insertions(+), 3 deletions(-) create mode 100644 examples/Makefile-gp712 create mode 100644 examples/platforms/gp712/Makefile.am create mode 100644 examples/platforms/gp712/README.md create mode 100644 examples/platforms/gp712/alarm.c create mode 100644 examples/platforms/gp712/alarm_qorvo.h create mode 100644 examples/platforms/gp712/diag.c create mode 100644 examples/platforms/gp712/flash.c create mode 100644 examples/platforms/gp712/logging.c create mode 100644 examples/platforms/gp712/misc.c create mode 100644 examples/platforms/gp712/openthread-core-gp712-config.h create mode 100644 examples/platforms/gp712/platform.c create mode 100644 examples/platforms/gp712/platform_qorvo.h create mode 100644 examples/platforms/gp712/radio.c create mode 100644 examples/platforms/gp712/radio_qorvo.h create mode 100644 examples/platforms/gp712/random.c create mode 100644 examples/platforms/gp712/random_qorvo.h create mode 100644 examples/platforms/gp712/uart-posix.c create mode 100644 examples/platforms/gp712/uart-socket.c create mode 100644 examples/platforms/gp712/uart_qorvo.h create mode 100644 third_party/Qorvo/LICENSE.txt create mode 100644 third_party/Qorvo/README.md create mode 100644 third_party/Qorvo/gp712/libQorvoRPi.a diff --git a/configure.ac b/configure.ac index 3f43a29d5..a1713a1f3 100644 --- a/configure.ac +++ b/configure.ac @@ -1084,11 +1084,11 @@ AC_DEFINE_UNQUOTED([OPENTHREAD_ENABLE_BORDER_ROUTER],[${OPENTHREAD_ENABLE_BORDER AC_ARG_WITH(examples, [AS_HELP_STRING([--with-examples=TARGET], - [Specify the examples from one of: none, posix, cc2538, cc2650, da15000, efr32, emsk, kw41z, nrf52840 @<:@default=none@:>@.])], + [Specify the examples from one of: none, posix, cc2538, cc2650, da15000, efr32, emsk, gp712, kw41z, nrf52840 @<:@default=none@:>@.])], [ case "${with_examples}" in - none|posix|cc2538|cc2650|da15000|efr32|emsk|kw41z|nrf52840) + none|posix|cc2538|cc2650|da15000|efr32|emsk|gp712|kw41z|nrf52840) ;; *) AC_MSG_ERROR([Invalid value ${with_examples} for --with-examples]) @@ -1131,6 +1131,11 @@ case ${with_examples} in AC_DEFINE_UNQUOTED([OPENTHREAD_EXAMPLES_EMSK],[${OPENTHREAD_EXAMPLES_EMSK}],[Define to 1 if you want to use emsk examples]) ;; + gp712) + OPENTHREAD_EXAMPLES_GP712=1 + AC_DEFINE_UNQUOTED([OPENTHREAD_EXAMPLES_GP712],[${OPENTHREAD_EXAMPLES_GP712}],[Define to 1 if you want to use gp712 examples]) + ;; + kw41z) OPENTHREAD_EXAMPLES_KW41Z=1 AC_DEFINE_UNQUOTED([OPENTHREAD_EXAMPLES_KW41Z],[${OPENTHREAD_EXAMPLES_KW41Z}],[Define to 1 if you want to use kw41z examples]) @@ -1140,7 +1145,6 @@ case ${with_examples} in OPENTHREAD_EXAMPLES_NRF52840=1 AC_DEFINE_UNQUOTED([OPENTHREAD_EXAMPLES_NRF52840],[${OPENTHREAD_EXAMPLES_NRF52840}],[Define to 1 if you want to use nrf52840 examples]) ;; - esac AC_MSG_CHECKING([whether to enable examples]) @@ -1168,6 +1172,9 @@ AM_CONDITIONAL([OPENTHREAD_EXAMPLES_EFR32], [test "${OPENTHREAD_EXAMPLES}" = "ef AC_SUBST(OPENTHREAD_EXAMPLES_EMSK) AM_CONDITIONAL([OPENTHREAD_EXAMPLES_EMSK], [test "${OPENTHREAD_EXAMPLES}" = "emsk"]) +AC_SUBST(OPENTHREAD_EXAMPLES_GP712) +AM_CONDITIONAL([OPENTHREAD_EXAMPLES_GP712], [test "${OPENTHREAD_EXAMPLES}" = "gp712"]) + AC_SUBST(OPENTHREAD_EXAMPLES_KW41Z) AM_CONDITIONAL([OPENTHREAD_EXAMPLES_KW41Z], [test "${OPENTHREAD_EXAMPLES}" = "kw41z"]) @@ -1335,6 +1342,7 @@ examples/platforms/cc2650/Makefile examples/platforms/da15000/Makefile examples/platforms/efr32/Makefile examples/platforms/emsk/Makefile +examples/platforms/gp712/Makefile examples/platforms/kw41z/Makefile examples/platforms/nrf52840/Makefile examples/platforms/posix/Makefile diff --git a/examples/Makefile-gp712 b/examples/Makefile-gp712 new file mode 100644 index 000000000..9969d75e7 --- /dev/null +++ b/examples/Makefile-gp712 @@ -0,0 +1,271 @@ +# +# 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. +# + +.NOTPARALLEL: + +TARGET = arm-bcm2708hardfp-linux-gnueabi +PPREFIX = gp712 +INFO = GP712 +#CROSS_COMPILE = $(TARGET)- + +AR = $(CROSS_COMPILE)ar +AS = $(CROSS_COMPILE)as +CPP = $(CROSS_COMPILE)cpp +CC = $(CROSS_COMPILE)gcc +CXX = $(CROSS_COMPILE)g++ +LD = $(CROSS_COMPILE)ld +STRIP = $(CROSS_COMPILE)strip +NM = $(CROSS_COMPILE)nm +RANLIB = $(CROSS_COMPILE)ranlib +OBJCOPY = $(CROSS_COMPILE)objcopy + +BuildJobs ?= 9 + +configure_OPTIONS = \ + --enable-cli-app=ftd \ + --enable-diag \ + --with-examples=$(PPREFIX) \ + --with-platform-info=$(INFO) \ + $(NULL) + +DEFAULT_LOGGING ?= 1 +include $(dir $(abspath $(lastword $(MAKEFILE_LIST))))/common-switches.mk + +ifeq ($(CLI_LOGGING),1) +configure_OPTIONS += --enable-cli-logging +endif + +GP712_CONFIG_FILE_CPPFLAGS = -DOPENTHREAD_PROJECT_CORE_CONFIG_FILE='\"openthread-core-gp712-config.h\"' +GP712_CONFIG_FILE_CPPFLAGS += -I$(PWD)/examples/platforms/gp712/ + +COMMONCFLAGS := \ + -fdata-sections \ + -ffunction-sections \ + -Os \ + -g \ + $(GP712_CONFIG_FILE_CPPFLAGS) \ + -W \ + -Wall \ + $(NULL) + +CPPFLAGS += \ + $(COMMONCFLAGS) \ + $(target_CPPFLAGS) \ + $(NULL) + +CFLAGS += \ + $(COMMONCFLAGS) \ + $(target_CFLAGS) \ + $(NULL) + +CXXFLAGS += \ + $(COMMONCFLAGS) \ + $(target_CXXFLAGS) \ + -fno-exceptions \ + -fno-rtti \ + $(NULL) + +LDFLAGS += \ + $(COMMONCFLAGS) \ + $(target_LDFLAGS) \ + -Wl,--gc-sections \ + -Wl,-Map=map.map \ + -lrt \ + -lpthread \ + $(NULL) + + +ECHO := @echo +MAKE := make +MKDIR_P := mkdir -p +LN_S := ln -s +RM_F := rm -f + +INSTALL := /usr/bin/install +INSTALLFLAGS := -p + +TopSourceDir := $(dir $(shell readlink $(firstword $(MAKEFILE_LIST)))).. +AbsTopSourceDir := $(dir $(realpath $(firstword $(MAKEFILE_LIST)))).. + +BuildPath = build +TopBuildDir = $(BuildPath) +AbsTopBuildDir = $(PWD)/$(TopBuildDir) + +ResultPath = output +TopResultDir = $(ResultPath) +AbsTopResultDir = $(PWD)/$(TopResultDir) + +TargetTuple = $(PPREFIX) + +ifndef BuildJobs +BuildJobs := $(shell getconf _NPROCESSORS_ONLN) +endif +JOBSFLAG := -j$(BuildJobs) + +PREFIX = $(PPREFIX) + +# +# configure-arch +# +# Configure OpenThread for the specified architecture. +# +# arch - The architecture to configure. +# +define configure-arch +$(ECHO) " CONFIG $(1)..." +(cd $(BuildPath)/$(1) && $(AbsTopSourceDir)/configure \ +INSTALL="$(INSTALL) $(INSTALLFLAGS)" \ +CPP="$(CPP)" CC="$(CC)" CXX="$(CXX)" OBJC="$(OBJC)" OBJCXX="$(OBJCXX)" AR="$(AR)" RANLIB="$(RANLIB)" NM="$(NM)" STRIP="$(STRIP)" CPPFLAGS="$(CPPFLAGS)" CFLAGS="$(CFLAGS)" CXXFLAGS="$(CXXFLAGS)" LDFLAGS="$(LDFLAGS)" \ +--prefix=/$(PREFIX) \ +--host=$(TARGET) \ +--target=$(TARGET) \ +--exec-prefix=/$(PREFIX) \ +--program-prefix=$(PREFIX)- \ +$(configure_OPTIONS)) +endef # configure-arch + +# +# build-arch +# +# Build the OpenThread intermediate build products for the specified +# architecture. +# +# arch - The architecture to build. +# +define build-arch +$(ECHO) " BUILD $(1)" +$(MAKE) $(JOBSFLAG) -C $(BuildPath)/$(1) -w \ +all +endef # build-arch + +# +# stage-arch +# +# Stage (install) the OpenThread final build products for the specified +# architecture. +# +# arch - The architecture to stage. +# +define stage-arch +$(ECHO) " STAGE $(1)" +$(MAKE) $(JOBSFLAG) -C $(BuildPath)/$(1) -w \ +DESTDIR=$(AbsTopResultDir) \ +install +endef # stage-arch + +# +# ARCH_template +# +# Define macros, targets and rules to configure, build, and stage the +# OpenThread for a single architecture. +# +# arch - The architecture to instantiate the template for. +# +define ARCH_template +CONFIGURE_TARGETS += configure-$(1) +BUILD_TARGETS += do-build-$(1) +STAGE_TARGETS += stage-$(1) +BUILD_DIRS += $(BuildPath)/$(1) +DIRECTORIES += $(BuildPath)/$(1) + +configure-$(1): target_CPPFLAGS=$($(1)_target_CPPFLAGS) +configure-$(1): target_CFLAGS=$($(1)_target_CFLAGS) +configure-$(1): target_CXXFLAGS=$($(1)_target_CXXFLAGS) +configure-$(1): target_LDFLAGS=$($(1)_target_LDFLAGS) + +configure-$(1): $(BuildPath)/$(1)/config.status + +$(BuildPath)/$(1)/config.status: | $(BuildPath)/$(1) + $$(call configure-arch,$(1)) + +do-build-$(1): configure-$(1) + +do-build-$(1): + +$$(call build-arch,$(1)) + +stage-$(1): do-build-$(1) + +stage-$(1): | $(TopResultDir) + $$(call stage-arch,$(1)) + +$(1): stage-$(1) +endef # ARCH_template + +.DEFAULT_GOAL := all + +all: stage + +# +# rpi_bcm2708 +# + +rpi_bcm2708_target_ABI = rpi_bcm2708 +rpi_bcm2708_target_CPPFLAGS = -march=armv6j -fomit-frame-pointer -fno-strict-aliasing -fno-pic -ffreestanding -mfloat-abi=hard -mfpu=vfp -pipe +rpi_bcm2708_target_CFLAGS = -march=armv6j -fomit-frame-pointer -fno-strict-aliasing -fno-pic -ffreestanding -mfloat-abi=hard -mfpu=vfp -pipe +rpi_bcm2708_target_CXXFLAGS = -march=armv6j -fomit-frame-pointer -fno-strict-aliasing -fno-pic -ffreestanding -mfloat-abi=hard -mfpu=vfp -pipe +rpi_bcm2708_target_LDFLAGS = -march=armv6j -fomit-frame-pointer -fno-strict-aliasing -fno-pic -ffreestanding -mfloat-abi=hard -mfpu=vfp -pipe + +# Instantiate an architecture-specific build template for each target +# architecture. + +$(foreach arch,$(TargetTuple),$(eval $(call ARCH_template,$(arch)))) + +# +# Common / Finalization +# + +configure: $(CONFIGURE_TARGETS) + +build: $(BUILD_TARGETS) + +stage: $(STAGE_TARGETS) + +DIRECTORIES = $(TopResultDir) $(TopResultDir)/$(TargetTuple)/lib $(BUILD_DIRS) + +CLEAN_DIRS = $(TopResultDir) $(BUILD_DIRS) + +all: stage + +$(DIRECTORIES): + $(ECHO) " MKDIR $@" + @$(MKDIR_P) "$@" + +clean: + $(ECHO) " CLEAN" + @$(RM_F) -r $(CLEAN_DIRS) + +help: + $(ECHO) "Simply type 'make -f $(firstword $(MAKEFILE_LIST))' to build OpenThread for the following " + $(ECHO) "architectures: " + $(ECHO) "" + $(ECHO) " $(TargetTuple)" + $(ECHO) "" + $(ECHO) "To build only a particular architecture, specify: " + $(ECHO) "" + $(ECHO) " make -f $(firstword $(MAKEFILE_LIST)) " + $(ECHO) "" diff --git a/examples/apps/cli/Makefile.am b/examples/apps/cli/Makefile.am index 63c26a0a5..f2cf41690 100644 --- a/examples/apps/cli/Makefile.am +++ b/examples/apps/cli/Makefile.am @@ -123,6 +123,14 @@ LDFLAGS_COMMON += \ $(NULL) endif # OPENTHREAD_EXAMPLES_EMSK +if OPENTHREAD_EXAMPLES_GP712 +LDADD_COMMON += \ + $(top_builddir)/examples/platforms/gp712/libopenthread-gp712.a \ + $(top_srcdir)/third_party/Qorvo/gp712/libQorvoRPi.a \ + $(NULL) + +endif # OPENTHREAD_EXAMPLES_GP712 + if OPENTHREAD_EXAMPLES_NRF52840 LDADD_COMMON += \ $(top_builddir)/examples/platforms/nrf52840/libopenthread-nrf52840.a \ diff --git a/examples/platforms/Makefile.am b/examples/platforms/Makefile.am index fd2b21446..9f0bc6128 100644 --- a/examples/platforms/Makefile.am +++ b/examples/platforms/Makefile.am @@ -36,6 +36,7 @@ DIST_SUBDIRS = \ da15000 \ efr32 \ emsk \ + gp712 \ kw41z \ nrf52840 \ posix \ @@ -68,6 +69,10 @@ if OPENTHREAD_EXAMPLES_EMSK SUBDIRS += emsk endif +if OPENTHREAD_EXAMPLES_GP712 +SUBDIRS += gp712 +endif + if OPENTHREAD_EXAMPLES_KW41Z SUBDIRS += kw41z endif @@ -88,6 +93,7 @@ PRETTY_SUBDIRS = \ da15000 \ efr32 \ emsk \ + gp712 \ kw41z \ nrf52840 \ posix \ diff --git a/examples/platforms/gp712/Makefile.am b/examples/platforms/gp712/Makefile.am new file mode 100644 index 000000000..9d7ced4f3 --- /dev/null +++ b/examples/platforms/gp712/Makefile.am @@ -0,0 +1,73 @@ +# +# Copyright (c) 2016-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 $(abs_top_nlbuild_autotools_dir)/automake/pre.am + +lib_LIBRARIES = libopenthread-gp712.a + +libopenthread_gp712_a_CPPFLAGS = \ + -I$(top_srcdir)/include \ + -I$(top_srcdir)/examples/platforms \ + -I$(top_srcdir)/examples/platforms/utils\ + -I$(top_srcdir)/src/core \ + -lrt \ + -lpthread \ + $(NULL) + +libopenthread_gp712_settings.cpp: $(srcdir)/../utils/settings.cpp + cp $? $@ + +CLEANFILES = libopenthread_gp712_settings.cpp + +libopenthread_gp712_a_SOURCES = \ + alarm.c \ + misc.c \ + logging.c \ + platform.c \ + radio.c \ + random.c \ + uart-posix.c \ + flash.c \ + libopenthread_gp712_settings.cpp \ + $(NULL) + +# uart-socket.c \ +# + +if OPENTHREAD_ENABLE_DIAG +libopenthread_gp712_a_SOURCES += \ + diag.c \ + $(NULL) +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/examples/platforms/gp712/README.md b/examples/platforms/gp712/README.md new file mode 100644 index 000000000..10b149281 --- /dev/null +++ b/examples/platforms/gp712/README.md @@ -0,0 +1,71 @@ +# OpenThread on Qorvo gp712 Example + +This directory contains example platform drivers for Qorvo gp712 on RPi. + +## Toolchain + +This example use the GNU GCC toolchain on the Raspberry Pi. +To build on the Pi: +1) Download the repo to the Pi +2) go to the subfolder in the openthread repo: third_party/nlbuild-autotools/repo/tools/packages and enter this command: +```bash +$ sudo /bin/bash build +``` +Note that you may need to install additional packages to make this build work, depending on your actual RPi OS version. +The build process will complain if additional packages are required. + +## Build Examples + +```bash +$ cd +$ ./bootstrap +$ CERT_LOG=1 CLI_LOGGING=1 COMMISSIONER=1 JOINER=1 DHCP6_CLIENT=1 DHCP6_SERVER=1 BORDER_ROUTER=1 make -f examples/Makefile-gp712 +``` + +After a successful build, the `elf` files are found in +`/output/gp712/bin`. + +Building a variant which interfaces via a tcp socket is also possible. Replace the uart-posix.c with uart-socket.c in the Makefile.am from examples/platforms/gp712/Makefile.am and rebuild. Now it should be possible to open a telnet to socket 9190 of the raspberry pi from a remote PC. This also easier testing with the official Thread Test Harness. + +## + +## Interact + +1. Spawn the process: + +```bash +$ cd /output/gp712/bin +$ ./gp712-ot-cli-ftd +``` + +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/examples/platforms/gp712/alarm.c b/examples/platforms/gp712/alarm.c new file mode 100644 index 000000000..900352a3f --- /dev/null +++ b/examples/platforms/gp712/alarm.c @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2016-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 implements the OpenThread platform abstraction for the alarm. + * + */ + +#include +#include +#include + +#include +#include +#include +#include +#include "alarm_qorvo.h" + +void qorvoAlarmInit(void) +{ +} + +uint32_t otPlatAlarmGetNow(void) +{ + return qorvoAlarmGetTimeMs(); +} + +static void qorvoAlarmFired(void *aInstance) +{ + otPlatAlarmFired((otInstance *)aInstance); +} + +void otPlatAlarmStartAt(otInstance *aInstance, uint32_t t0, uint32_t dt) +{ + (void)t0; + qorvoAlarmUnScheduleEventArg((qorvoAlarmCallback_t)qorvoAlarmFired, aInstance); + qorvoAlarmScheduleEventArg(dt * 1000, qorvoAlarmFired, aInstance); +} + +void otPlatAlarmStop(otInstance *aInstance) +{ + qorvoAlarmUnScheduleEventArg((qorvoAlarmCallback_t)qorvoAlarmFired, aInstance); +} + +void qorvoAlarmUpdateTimeout(struct timeval *aTimeout) +{ + (void)aTimeout; +} + +void qorvoAlarmProcess(void) +{ +} diff --git a/examples/platforms/gp712/alarm_qorvo.h b/examples/platforms/gp712/alarm_qorvo.h new file mode 100644 index 000000000..c818ceec1 --- /dev/null +++ b/examples/platforms/gp712/alarm_qorvo.h @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2016-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 the declarations of the Alarm functions from the Qorvo library. + * + */ + +#ifndef ALARM_QORVO_H_ +#define ALARM_QORVO_H_ + +#include +#include +#include +#include + +typedef void (*qorvoAlarmCallback_t)( void* ); + +/** + * This function initializes the alarm service used by OpenThread. + * + */ +void qorvoAlarmInit(void); + +/** + * This function retrieves the time remaining until the alarm fires. + * + * @param[out] aTimeval A pointer to the timeval struct. + * + */ +void qorvoAlarmUpdateTimeout(struct timeval *aTimeout); + +/** + * This function performs alarm driver processing. + * + */ +void qorvoAlarmProcess(void); + +/** + * This function retrieves the current time. + * + * @param[out] The current time in ms. + * + */ +uint32_t qorvoAlarmGetTimeMs(void); + +/** + * This function schedules a callback after a relative amount of time. + * + * @param[in] rel_time The relative time in ms. + * @param[in] callback A callback function which will be called. + * @param[in] arg A context pointer which will be passed as an argument to the callback. + * + */ +void qorvoAlarmScheduleEventArg(uint32_t rel_time, qorvoAlarmCallback_t callback, void* arg); + +/** + * This function unschedules the callback. + * + * @param[in] callback A callback function which will be removed from the list. + * @param[in] arg A context pointer which will be passed as an argument to the callback. + * + */ +bool qorvoAlarmUnScheduleEventArg(qorvoAlarmCallback_t callback, void* arg); + +#endif // ALARM_QORVO_H_ diff --git a/examples/platforms/gp712/diag.c b/examples/platforms/gp712/diag.c new file mode 100644 index 000000000..268ff6e45 --- /dev/null +++ b/examples/platforms/gp712/diag.c @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2016-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 + +/** + * 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; +} diff --git a/examples/platforms/gp712/flash.c b/examples/platforms/gp712/flash.c new file mode 100644 index 000000000..0d17ed3f1 --- /dev/null +++ b/examples/platforms/gp712/flash.c @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2016-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. + */ + +#define _XOPEN_SOURCE 500 + +#include "platform_qorvo.h" + +#include +#include +#include +#include +#include +#include + +#include + +#include "utils/code_utils.h" +#include "utils/flash.h" + +static int sFlashFd; +uint32_t sEraseAddress; + +#define FLASH_SIZE 0x40000 +#define FLASH_PAGE_SIZE 0x800 +#define FLASH_PAGE_NUM 128 + +otError utilsFlashInit(void) +{ + otError error = OT_ERROR_NONE; + char fileName[20]; + struct stat st; + bool create = false; + + memset(&st, 0, sizeof(st)); + + if (stat("tmp", &st) == -1) + { + mkdir("tmp", 0777); + } + + snprintf(fileName, sizeof(fileName), "tmp/node.flash"); + + 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 ]; + ssize_t r; + + 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 + r = pwrite(sFlashFd, &(dummyPage[0]), FLASH_PAGE_SIZE, (off_t)address); + otEXPECT_ACTION((r) == ((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++) + { + ret = utilsFlashRead(aAddress + index, &byte, 1); + otEXPECT(ret == 1); + // Use bitwise AND to emulate the behavior of flash memory + byte &= aData[index]; + ret = (uint32_t)pwrite(sFlashFd, &byte, 1, (off_t)(aAddress + index)); + otEXPECT(ret == 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/examples/platforms/gp712/logging.c b/examples/platforms/gp712/logging.c new file mode 100644 index 000000000..69358f1a6 --- /dev/null +++ b/examples/platforms/gp712/logging.c @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2016-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 "platform_qorvo.h" + +#include +#include +#include +#include +#include +#include + +#ifndef _WIN32 +#include +#endif + +#include + +#include "utils/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) + +int PlatOtLogLevelToSysLogLevel(otLogLevel aLogLevel) +{ + int sysloglevel; + + switch (aLogLevel) + { + case OT_LOG_LEVEL_NONE: + sysloglevel = LOG_ERR; + break; + + case OT_LOG_LEVEL_CRIT: + sysloglevel = LOG_CRIT; + break; + + case OT_LOG_LEVEL_WARN: + sysloglevel = LOG_WARNING; + break; + + case OT_LOG_LEVEL_INFO: + sysloglevel = LOG_INFO; + break; + + case OT_LOG_LEVEL_DEBG: + sysloglevel = LOG_DEBUG; + break; + + default: + sysloglevel = LOG_ERR; + } + + return sysloglevel; +} + +void otPlatLog(otLogLevel aLogLevel, otLogRegion aLogRegion, const char *aFormat, ...) +{ + char logString[512]; + unsigned int offset; + int charsWritten; + va_list args; + + offset = 0; + + va_start(args, aFormat); + charsWritten = vsnprintf(&logString[offset], sizeof(logString) - offset, aFormat, args); + va_end(args); + + otEXPECT_ACTION(charsWritten >= 0, logString[offset] = 0); + +exit: + syslog(PlatOtLogLevelToSysLogLevel(aLogLevel), "%s", logString); + + (void)aLogRegion; +} + diff --git a/examples/platforms/gp712/misc.c b/examples/platforms/gp712/misc.c new file mode 100644 index 000000000..42f1a2818 --- /dev/null +++ b/examples/platforms/gp712/misc.c @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2016-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 "platform_qorvo.h" + +#include +#include + +#include +#include +#include "radio_qorvo.h" +#include + +extern int gArgumentsCount; +extern char **gArguments; + +extern void platformUartRestore(void); + +void otPlatReset(otInstance *aInstance) +{ + char *argv[gArgumentsCount + 1]; + + for (int i = 0; i < gArgumentsCount; ++i) + { + argv[i] = gArguments[i]; + } + + argv[gArgumentsCount] = NULL; + + qorvoRadioReset(); + platformUartRestore(); + + execvp(argv[0], argv); + perror("reset failed"); + exit(EXIT_FAILURE); + (void)aInstance; +} + +otPlatResetReason otPlatGetResetReason(otInstance *aInstance) +{ + (void)aInstance; + return OT_PLAT_RESET_REASON_POWER_ON; +} + +void otPlatWakeHost(void) +{ + // TODO: implement an operation to wake the host from sleep state. +} diff --git a/examples/platforms/gp712/openthread-core-gp712-config.h b/examples/platforms/gp712/openthread-core-gp712-config.h new file mode 100644 index 000000000..d9c7ba8a0 --- /dev/null +++ b/examples/platforms/gp712/openthread-core-gp712-config.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2016-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 gp712 compile-time configuration constants for OpenThread. + */ + +#ifndef OPENTHREAD_CORE_GP712_CONFIG_H_ +#define OPENTHREAD_CORE_GP712_CONFIG_H_ + +/** + * @def OPENTHREAD_CONFIG_LEGACY_TRANSMIT_DONE + * + * Define to 1 if you want use legacy transmit done. + * + */ +#define OPENTHREAD_CONFIG_LEGACY_TRANSMIT_DONE 1 + + +#endif // OPENTHREAD_CORE_GP712_CONFIG_H_ diff --git a/examples/platforms/gp712/platform.c b/examples/platforms/gp712/platform.c new file mode 100644 index 000000000..68315863e --- /dev/null +++ b/examples/platforms/gp712/platform.c @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2016-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 + * @brief + * This file includes the platform-specific initializers. + */ + +#include "platform_qorvo.h" + +#include "stdio.h" +#include "stdlib.h" + +#include +#include + +#include "radio_qorvo.h" +#include "random_qorvo.h" +#include "uart_qorvo.h" + +void platformUartInit(void); +void platformUartProcess(void); + +otInstance *localInstance = NULL; + +int gArgumentsCount = 0; +char **gArguments = NULL; + +bool qorvoPlatGotoSleepCheck(void) +{ + bool canGotoSleep = false; + + if (localInstance) + { + canGotoSleep = !otTaskletsArePending(localInstance); + } + + return canGotoSleep; +} + +void PlatformInit(int argc, char *argv[]) +{ + gArgumentsCount = argc; + gArguments = argv; + + qorvoPlatInit((qorvoPlatGotoSleepCheckCallback_t)qorvoPlatGotoSleepCheck); + platformUartInit(); + //qorvoAlarmInit(); + qorvoRandomInit(); + qorvoRadioInit(); + +} + +void PlatformProcessDrivers(otInstance *aInstance) +{ + if (localInstance == NULL) + { + // local copy in case we need to perform a callback. + localInstance = aInstance; + } + + qorvoPlatMainLoop(!otTaskletsArePending(aInstance)); + platformUartProcess(); + //qorvoRadioProcess(); + //qorvoAlarmProcess(); + +} diff --git a/examples/platforms/gp712/platform_qorvo.h b/examples/platforms/gp712/platform_qorvo.h new file mode 100644 index 000000000..7bfed1b95 --- /dev/null +++ b/examples/platforms/gp712/platform_qorvo.h @@ -0,0 +1,77 @@ +/* + * 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 the platform-specific initializers. + * + */ + +#ifndef PLATFORM_QORVO_H_ +#define PLATFORM_QORVO_H_ + +#include +#include + +typedef void (*qorvoPlatPollFunction_t)(uint8_t); +typedef uint8_t (*qorvoPlatGotoSleepCheckCallback_t) (void); + +/** + * This function registers a callback to a file descriptor. + * + * @param[in] fd The file descriptor. + * @param[in] pollFunction The callback which should be called when data is ready or needed for the file descriptor. + * + */ +void qorvoPlatRegisterPollFunction(int fd, qorvoPlatPollFunction_t pollFunction); + +/** + * This function unregisters a callback for a file descriptor. + * + * @param[in] fd The file descriptor. + * + */ +void qorvoPlatUnRegisterPollFunction(int fd); + +/** + * This function initializes the platform. + * + * @param[in] gotoSleepCheckCallback The callback which needs to return if sleep is allowed. + * + */ +void qorvoPlatInit(qorvoPlatGotoSleepCheckCallback_t gotoSleepCheckCallback); + +/** + * This function runs the main loop of the platform once. + * + * @param[in] canGoToSleep Indicates if the platform can got to sleep. + * + */ +void qorvoPlatMainLoop(bool canGoToSleep); + +#endif // PLATFORM_QORVO_H_ diff --git a/examples/platforms/gp712/radio.c b/examples/platforms/gp712/radio.c new file mode 100644 index 000000000..dc473b808 --- /dev/null +++ b/examples/platforms/gp712/radio.c @@ -0,0 +1,312 @@ +/* + * Copyright (c) 2016-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 implements the OpenThread platform abstraction for radio communication. + * + */ + +#include + +#include +#include + +#include +#include + +#include "utils/code_utils.h" + +#include "radio_qorvo.h" + +enum +{ + GP712_RECEIVE_SENSITIVITY = -100, // dBm +}; + +enum +{ + IEEE802154_MIN_LENGTH = 5, + IEEE802154_MAX_LENGTH = 127, + IEEE802154_ACK_LENGTH = 5, + IEEE802154_FRAME_TYPE_MASK = 0x7, + IEEE802154_FRAME_TYPE_ACK = 0x2, + IEEE802154_FRAME_PENDING = 1 << 4, + IEEE802154_ACK_REQUEST = 1 << 5, + IEEE802154_DSN_OFFSET = 2, +}; + +enum +{ + QORVO_RSSI_OFFSET = 73, + QORVO_CRC_BIT_MASK = 0x80, + QORVO_LQI_BIT_MASK = 0x7f, +}; + +extern otRadioFrame sTransmitFrame; + +static otRadioState sState; +static otInstance *pQorvoInstance; + +typedef struct otCachedSettings_s +{ + uint16_t panid; +} otCachedSettings_t; + +static otCachedSettings_t otCachedSettings; + +static uint8_t sScanstate = 0; +static int8_t sLastReceivedPower = 127; + +void otPlatRadioGetIeeeEui64(otInstance *aInstance, uint8_t *aIeeeEui64) +{ + (void)aInstance; + qorvoRadioGetIeeeEui64(aIeeeEui64); +} + +void otPlatRadioSetPanId(otInstance *aInstance, uint16_t panid) +{ + (void)aInstance; + qorvoRadioSetPanId(panid); + otCachedSettings.panid = panid; +} + +void otPlatRadioSetExtendedAddress(otInstance *aInstance, uint8_t *address) +{ + (void)aInstance; + qorvoRadioSetExtendedAddress(address); +} + +void otPlatRadioSetShortAddress(otInstance *aInstance, uint16_t address) +{ + (void)aInstance; + qorvoRadioSetShortAddress(address); +} + +bool otPlatRadioIsEnabled(otInstance *aInstance) +{ + (void)aInstance; + return (sState != OT_RADIO_STATE_DISABLED); +} + +otError otPlatRadioEnable(otInstance *aInstance) +{ + pQorvoInstance = aInstance; + memset(&otCachedSettings, 0x00, sizeof(otCachedSettings_t)); + + if (!otPlatRadioIsEnabled(aInstance)) + { + sState = OT_RADIO_STATE_SLEEP; + } + + return OT_ERROR_NONE; +} + +otError otPlatRadioDisable(otInstance *aInstance) +{ + (void)aInstance; + otEXPECT(otPlatRadioIsEnabled(aInstance)); + + if (sState == OT_RADIO_STATE_RECEIVE) + { + qorvoRadioSetRxOnWhenIdle(false); + } + + sState = OT_RADIO_STATE_DISABLED; + +exit: + return OT_ERROR_NONE; +} + +otError otPlatRadioSleep(otInstance *aInstance) +{ + (void)aInstance; + otError error = OT_ERROR_INVALID_STATE; + + if (sState == OT_RADIO_STATE_RECEIVE) + { + qorvoRadioSetRxOnWhenIdle(false); + error = OT_ERROR_NONE; + sState = OT_RADIO_STATE_SLEEP; + } + + return error; +} + +otError otPlatRadioReceive(otInstance *aInstance, uint8_t aChannel) +{ + otError error = OT_ERROR_INVALID_STATE; + pQorvoInstance = aInstance; + + if ((sState != OT_RADIO_STATE_DISABLED) && (sScanstate == 0)) + { + qorvoRadioSetCurrentChannel(aChannel); + error = OT_ERROR_NONE; + } + + if (sState == OT_RADIO_STATE_SLEEP) + { + qorvoRadioSetRxOnWhenIdle(true); + error = OT_ERROR_NONE; + sState = OT_RADIO_STATE_RECEIVE; + } + + return error; + +} + +otError otPlatRadioTransmit(otInstance *aInstance, otRadioFrame *aPacket) +{ + otError err = OT_ERROR_NONE; + + pQorvoInstance = aInstance; + + otEXPECT_ACTION(sState != OT_RADIO_STATE_DISABLED, err = OT_ERROR_INVALID_STATE); + + err = qorvoRadioTransmit(aPacket); + +exit: + return err; +} + +void cbQorvoRadioTransmitDone(otRadioFrame *aPacket, bool aFramePending, otError aError) +{ + otPlatRadioTransmitDone(pQorvoInstance, aPacket, aFramePending, aError); +} + +void cbQorvoRadioReceiveDone(otRadioFrame *aPacket, otError aError) +{ + if (aError == OT_ERROR_NONE) + { + sLastReceivedPower = aPacket->mPower; + } + + otPlatRadioReceiveDone(pQorvoInstance, aPacket, aError); +} + +otRadioFrame *otPlatRadioGetTransmitBuffer(otInstance *aInstance) +{ + (void)aInstance; + return &sTransmitFrame; +} + +int8_t otPlatRadioGetRssi(otInstance *aInstance) +{ + (void)aInstance; + return sLastReceivedPower; +} + +otRadioCaps otPlatRadioGetCaps(otInstance *aInstance) +{ + (void)aInstance; + return OT_RADIO_CAPS_ACK_TIMEOUT | OT_RADIO_CAPS_ENERGY_SCAN | OT_RADIO_CAPS_TRANSMIT_RETRIES; +} + +bool otPlatRadioGetPromiscuous(otInstance *aInstance) +{ + (void)aInstance; + return false; +} + +void otPlatRadioSetPromiscuous(otInstance *aInstance, bool aEnable) +{ + (void)aInstance; + (void)aEnable; +} + + +void otPlatRadioEnableSrcMatch(otInstance *aInstance, bool aEnable) +{ + (void)aInstance; + qorvoRadioEnableSrcMatch(aEnable); +} + +otError otPlatRadioAddSrcMatchShortEntry(otInstance *aInstance, const uint16_t aShortAddress) +{ + (void)aInstance; + return qorvoRadioAddSrcMatchShortEntry(aShortAddress, otCachedSettings.panid); +} + +otError otPlatRadioAddSrcMatchExtEntry(otInstance *aInstance, const uint8_t *aExtAddress) +{ + (void)aInstance; + return qorvoRadioAddSrcMatchExtEntry(aExtAddress, otCachedSettings.panid); +} + +otError otPlatRadioClearSrcMatchShortEntry(otInstance *aInstance, const uint16_t aShortAddress) +{ + (void)aInstance; + return qorvoRadioClearSrcMatchShortEntry(aShortAddress, otCachedSettings.panid); +} + +otError otPlatRadioClearSrcMatchExtEntry(otInstance *aInstance, const uint8_t *aExtAddress) +{ + (void)aInstance; + return qorvoRadioClearSrcMatchExtEntry(aExtAddress, otCachedSettings.panid); +} + +void otPlatRadioClearSrcMatchShortEntries(otInstance *aInstance) +{ + (void)aInstance; + /* clear both short and extended addresses here */ + qorvoRadioClearSrcMatchEntries(); +} + +void otPlatRadioClearSrcMatchExtEntries(otInstance *aInstance) +{ + (void)aInstance; + /* not implemented */ + /* assumes clearing of short and extended entries is done simultaniously by the openthread stack */ +} + +otError otPlatRadioEnergyScan(otInstance *aInstance, uint8_t aScanChannel, uint16_t aScanDuration) +{ + (void)aInstance; + sScanstate = 1; + return qorvoRadioEnergyScan(aScanChannel, aScanDuration); +} + +void cbQorvoRadioEnergyScanDone(int8_t aEnergyScanMaxRssi) +{ + sScanstate = 0; + otPlatRadioEnergyScanDone(pQorvoInstance, aEnergyScanMaxRssi); +} + + +void otPlatRadioSetDefaultTxPower(otInstance *aInstance, int8_t aPower) +{ + // TODO: Create a proper implementation for this driver. + (void)aInstance; + (void)aPower; +} + +int8_t otPlatRadioGetReceiveSensitivity(otInstance *aInstance) +{ + (void)aInstance; + return GP712_RECEIVE_SENSITIVITY; +} diff --git a/examples/platforms/gp712/radio_qorvo.h b/examples/platforms/gp712/radio_qorvo.h new file mode 100644 index 000000000..84c4f273a --- /dev/null +++ b/examples/platforms/gp712/radio_qorvo.h @@ -0,0 +1,202 @@ +/* + * 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 the declarations of the radio functions from the Qorvo library. + * + */ + +#ifndef RADIO_QORVO_H_ +#define RADIO_QORVO_H_ + +#include +#include +#include + +/** + * This function initializes the radio. + * + */ +void qorvoRadioInit(void); + +/** + * This function resets the radio. + * + */ +void qorvoRadioReset(void); + +/** + * This function processes event to/from the radio. + * + */ +void qorvoRadioProcess(void); + +/** + * This function starts an ED scan. + * + * @param[in] aScanChannel The channel which needs to be scanned. + * @param[in] aScanDuration The amount of time in ms which needs to be scanned. + * + */ +otError qorvoRadioEnergyScan(uint8_t aScanChannel, uint16_t aScanDuration); + +/** + * This function sets the current channel. + * + * @param[in] channel The channel index. + * + */ +void qorvoRadioSetCurrentChannel(uint8_t channel); + +/** + * This function sets the idle behaviour of the radio. + * + * @param[in] rxOnWhenIdle If true, the radio remains on which not transmitting. + * + */ +void qorvoRadioSetRxOnWhenIdle(bool rxOnWhenIdle); + +/** + * This function retrieves the MAC address of the radio. + * + * @param[out] aIeeeEui64 The MAC address of the radio. + * + */ +void qorvoRadioGetIeeeEui64(uint8_t *aIeeeEui64); + +/** + * This function transmits a frame. + * + * @param[in] aPacket The frame which needs to be transmitted. + * + */ +otError qorvoRadioTransmit(otRadioFrame *aPacket); + +/** + * This function sets the PanId. + * + * @param[in] panid The panId. + * + */ +void qorvoRadioSetPanId(uint16_t panid); + +/** + * This function sets the short address. + * + * @param[in] address The short address. + * + */ +void qorvoRadioSetShortAddress(uint16_t address); + +/** + * This function sets the extended address. + * + * @param[in] address The extended address. + * + */ +void qorvoRadioSetExtendedAddress(uint8_t *address); + +/** + * This function enables source address matching for indirect transmit. + * + * @param[in] aEnable if True will enable source address matching, false will disable. + * + */ +void qorvoRadioEnableSrcMatch(bool aEnable); + +/** + * This function clears all entries from the source address match list. + * + */ +void qorvoRadioClearSrcMatchEntries(void); + +/** + * This function adds an short address plus panid to the source address match list. + * + * @param[in] aShortAddress The short address which should be added. + * @param[in] panid The panid. + * + */ +otError qorvoRadioAddSrcMatchShortEntry(const uint16_t aShortAddress, uint16_t panid); + +/** + * This function adds an extended address plus panid to the source address match list. + * + * @param[in] aExtAddress The extended address which should be added. + * @param[in] panid The panid. + * + */ +otError qorvoRadioAddSrcMatchExtEntry(const uint8_t *aExtAddress, uint16_t panid); + +/** + * This function removes an short address plus panid from the source address match list. + * + * @param[in] aShortAddress The short address which should be removed. + * @param[in] panid The panid. + * + */ +otError qorvoRadioClearSrcMatchShortEntry(const uint16_t aShortAddress, uint16_t panid); + +/** + * This function removes an extended address plus panid from the source address match list. + * + * @param[in] aExtAddress The extended address which should be removed. + * @param[in] panid The panid. + * + */ +otError qorvoRadioClearSrcMatchExtEntry(const uint8_t *aExtAddress, uint16_t panid); + +/** + * This callback is called when the energy scan is finished. + * + * @param[in] aEnergyScanMaxRssi The amount of energy detected during the ED scan. + * + */ +void cbQorvoRadioEnergyScanDone(int8_t aEnergyScanMaxRssi); + +/** + * This callback is called after a transmission is completed (and if required an ACK is received). + * + * @param[in] aPacket The packet which was transmitted. + * @param[in] aFramePending Indicates if the FP bit was set in the ACK frame or not. + * @param[in] aError Indicates if an error occurred during transmission. + * + */ +void cbQorvoRadioTransmitDone(otRadioFrame *aPacket, bool aFramePending, otError aError); + +/** + * This callback is called after a frame is received. + * + * @param[in] aPacket The packet which was received. + * @param[in] aError Any error which occurred during reception of the packet. + * + */ +void cbQorvoRadioReceiveDone(otRadioFrame *aPacket, otError aError); + +#endif // RADIO_QORVO_H_ diff --git a/examples/platforms/gp712/random.c b/examples/platforms/gp712/random.c new file mode 100644 index 000000000..757be1507 --- /dev/null +++ b/examples/platforms/gp712/random.c @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2016-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 implements a pseudo-random number generator. + * + */ + +#include +#include + + +#include +#include + +#include "utils/code_utils.h" + +static uint32_t sState = 1; + +void qorvoRandomInit(void) +{ +#if __SANITIZE_ADDRESS__ == 0 + + otError error; + + error = otPlatRandomGetTrue((uint8_t *)&sState, sizeof(sState)); + assert(error == OT_ERROR_NONE); + +#else // __SANITIZE_ADDRESS__ + + sState = (uint32_t)time(NULL); + +#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/examples/platforms/gp712/random_qorvo.h b/examples/platforms/gp712/random_qorvo.h new file mode 100644 index 000000000..b4703e8fc --- /dev/null +++ b/examples/platforms/gp712/random_qorvo.h @@ -0,0 +1,57 @@ +/* + * 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 the declarations of the random functions from the Qorvo library. + * + */ + +#ifndef RANDOM_QORVO_H_ +#define RANDOM_QORVO_H_ + +#include +#include + +/** + * This function initializes the random number service used by OpenThread. + * + */ +void qorvoRandomInit(void); + +/** + * This function returns an array of random numbers. + * + * @param[out] aOutput Pointer to an array which will be filled with random data. + * @param[in] aOutputLength number of bytes which will be written into the array. + * + */ +void qorvoRandomGet(uint8_t *aOutput, uint8_t aOutputLength); + + +#endif // RANDOM_QORVO_H_ diff --git a/examples/platforms/gp712/uart-posix.c b/examples/platforms/gp712/uart-posix.c new file mode 100644 index 000000000..7b991a45f --- /dev/null +++ b/examples/platforms/gp712/uart-posix.c @@ -0,0 +1,275 @@ +/* + * Copyright (c) 2016-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 "platform_qorvo.h" +#include "alarm_qorvo.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "utils/code_utils.h" + +#ifdef OPENTHREAD_TARGET_LINUX +#include +int posix_openpt(int oflag); +int grantpt(int fildes); +int unlockpt(int fd); +char *ptsname(int fd); +#endif // OPENTHREAD_TARGET_LINUX + +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 platformDummy(void *dummyPointer) +{ + (void)dummyPointer; +} + +static void cbKeyPressed(uint8_t Param) +{ + (void) Param; + qorvoAlarmScheduleEventArg(0, platformDummy, (void *) &s_in_fd); +} + +void platformUartRestore(void) +{ + restore_stdin_termios(); + restore_stdout_termios(); + dup2(s_out_fd, STDOUT_FILENO); +} + +void platformUartInit(void) +{ + s_in_fd = dup(STDIN_FILENO); + s_out_fd = dup(STDOUT_FILENO); + dup2(STDERR_FILENO, STDOUT_FILENO); + + qorvoPlatRegisterPollFunction(s_in_fd, cbKeyPressed); + qorvoPlatRegisterPollFunction(s_out_fd, cbKeyPressed); +} + +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 + + // 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); + } + +exit: + + if (error != OT_ERROR_NONE) + { + 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 platformUartProcess(void) +{ + ssize_t rval; + const int error_flags = POLLERR | POLLNVAL | POLLHUP; + struct pollfd pollfd[] = + { + { s_in_fd, POLLIN | error_flags, 0 }, + { s_out_fd, POLLOUT | error_flags, 0 }, + }; + + errno = 0; + + rval = poll(pollfd, sizeof(pollfd) / sizeof(*pollfd), 0); + + if (rval < 0) + { + perror("poll"); + exit(EXIT_FAILURE); + } + + if (rval > 0) + { + if ((pollfd[0].revents & error_flags) != 0) + { + perror("s_in_fd"); + exit(EXIT_FAILURE); + } + + if ((pollfd[1].revents & error_flags) != 0) + { + perror("s_out_fd"); + exit(EXIT_FAILURE); + } + + if (pollfd[0].revents & POLLIN) + { + rval = read(s_in_fd, s_receive_buffer, sizeof(s_receive_buffer)); + + if (rval <= 0) + { + perror("read"); + exit(EXIT_FAILURE); + } + + otPlatUartReceived(s_receive_buffer, (uint16_t)rval); + } + + if ((s_write_length > 0) && (pollfd[1].revents & POLLOUT)) + { + 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(); + } + } + } +} diff --git a/examples/platforms/gp712/uart-socket.c b/examples/platforms/gp712/uart-socket.c new file mode 100644 index 000000000..633ab223b --- /dev/null +++ b/examples/platforms/gp712/uart-socket.c @@ -0,0 +1,337 @@ +/* + * Copyright (c) 2016-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 implements the OpenThread platform abstraction for cli over ip socket communication. + * + */ + +#include "platform_qorvo.h" +#include "alarm_qorvo.h" +#include "uart_qorvo.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include + +#include "utils/code_utils.h" + +#define BUFFER_MAX_SIZE 255 +#define SOCKET_PORT 9190 +#define SOCKET_WRITE(socketInfo, buf, length) sendto(socketInfo.socketId, (const char*)buf, length, 0, &socketInfo.addr, sizeof(socketInfo.addr)) +#define SOCKET_READ(socketId, buf, length) recv(socketId, buf, length, 0) + +typedef struct +{ + uint32_t socketId; + bool isValid; + struct sockaddr addr; + pthread_t rfReadThread; +} PlatSocket_t; + +PlatSocket_t PlatSocketConnection; +int PlatSocketPipeFd [2]; +int PlatServerSocketId; + +void PlatSocketRxNewConn(uint8_t id); +void PlatSocketInit(void); +void PlatSocketDeInit(void); +int PlatSocketTxData(uint16_t length, uint8_t *pData, uint32_t socketId); + +#define PLAT_UART_MAX_CHAR 1024 + +uint32_t PlatSocketId = 0; + +void PlatSocketSendInput(void *buffer) +{ + uint8_t len = 0; + uint8_t *buf = (uint8_t *) buffer; + len = strlen((char *)buf); + otPlatUartReceived((uint8_t *) buf, (uint16_t)len); + free(buf); + buf = 0; + len = 0; +} + +void PlatSocketRx(uint16_t length, const char *buffer, uint32_t socketId) +{ + uint8_t *buf = 0; + PlatSocketId = socketId; + + if (length > 0) + { + buf = malloc(length + 2); + memcpy(buf, buffer, length); + buf[length] = '\n'; + buf[length + 1] = 0; + qorvoAlarmScheduleEventArg(0, PlatSocketSendInput, (void *) buf); + } +} + +void PlatSocketClose(uint32_t socketId) +{ + (void)socketId; +} + +otError otPlatUartEnable(void) +{ + PlatSocketInit(); + return OT_ERROR_NONE; +} + +otError otPlatUartDisable(void) +{ + PlatSocketDeInit(); + return OT_ERROR_NONE; +} + +otError otPlatUartSend(const uint8_t *aBuf, uint16_t aBufLength) +{ + otError error = OT_ERROR_NONE; + char localbuf[PLAT_UART_MAX_CHAR]; + + memcpy(localbuf, aBuf, aBufLength); + localbuf[aBufLength] = 0; + printf("%s", localbuf); + + if (PlatSocketId) + { + PlatSocketTxData(aBufLength, (uint8_t *) aBuf, PlatSocketId); + } + + otPlatUartSendDone(); + return error; +} + +void platformUartInit(void) +{ +} + +void platformUartProcess(void) +{ +} + +int PlatSocketListenForClients() +{ + //Setup server side socket + int sockfd; + struct sockaddr_in serv_addr; + uint32_t flag = 1; + int ret; + + sockfd = socket(AF_INET, SOCK_STREAM, 0); + otEXPECT_ACTION(sockfd >= 0, sockfd = -1); + + // disable Nagle's algorithm to avoid long latency + setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, (char *)&flag, sizeof(flag)); + // allow reuse of the same address + setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char *)&flag, sizeof(flag)); + memset(&serv_addr, 0, sizeof(serv_addr)); + + serv_addr.sin_addr.s_addr = INADDR_ANY; + serv_addr.sin_family = AF_INET; + serv_addr.sin_port = htons(SOCKET_PORT); + ret = bind(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)); + otEXPECT_ACTION(ret >= 0, close(sockfd); sockfd = -1); + ret = listen(sockfd, 10); + otEXPECT_ACTION(ret != -1, exit(1)); +exit: + return sockfd; +} + +void PlatSocketRxSignaled(uint8_t id) +{ + (void)(id); + //Dummy callback function to flush pipe + uint8_t readChar; + //Remove trigger byte from pipe + read(PlatSocketPipeFd [0], &readChar, 1); +} + +void *PlatSocketReadThread(void *pClientSocket) +{ + char buffer[BUFFER_MAX_SIZE]; + PlatSocket_t *clientSocket = ((PlatSocket_t *)pClientSocket); + + memset(buffer, 0, BUFFER_MAX_SIZE); + + while (1) + { + int readLen = SOCKET_READ(clientSocket->socketId, buffer, BUFFER_MAX_SIZE); + + if (readLen < 0) + { + perror("Reading socket"); + break; + } + else + { + if (readLen == 0) + { + break; + } + + { + uint8_t someByte = 0x12; //No functional use only using pipe to kick main thread + + PlatSocketRx(readLen, buffer, clientSocket->socketId); + + write(PlatSocketPipeFd [1], &someByte, 1); //[1] = write fd + } + } + } + + clientSocket->isValid = 0; + qorvoPlatUnRegisterPollFunction(clientSocket->socketId); + close(clientSocket->socketId); + + PlatSocketClose(clientSocket->socketId); + + return NULL; +} + +void PlatSocketRxNewConn(uint8_t id) +{ + //Find first non-valid client in list - add here + if (PlatSocketConnection.isValid == 0) + { + //Add new client to client list + socklen_t len; + len = sizeof(PlatSocketConnection.addr); + int retval = accept(id, (struct sockaddr *)&PlatSocketConnection.addr, (socklen_t *) &len); + + if (retval >= 0) + { + int retErr; + PlatSocketConnection.socketId = retval; + retErr = pthread_create(&PlatSocketConnection.rfReadThread, + NULL, + PlatSocketReadThread, + &PlatSocketConnection); + + if (retErr) + { + close(PlatSocketConnection.socketId); + } + else + { + PlatSocketConnection.isValid = 1; + } + } + } + else + { + + int tempfd; + tempfd = accept(id, (struct sockaddr *)NULL, NULL); + + if (tempfd >= 0) + { + close(tempfd); + } + } +} + +/***************************************************************************** + * Public Function Definitions + *****************************************************************************/ + +void PlatSocketInit(void) +{ + memset(&PlatSocketConnection, 0, sizeof(PlatSocketConnection)); + + // in case we are a server, setup listening for client + PlatServerSocketId = PlatSocketListenForClients(); + qorvoPlatRegisterPollFunction(PlatServerSocketId, PlatSocketRxNewConn); + + // hack + pipe(PlatSocketPipeFd); + qorvoPlatRegisterPollFunction(PlatSocketPipeFd [0], PlatSocketRxSignaled); +} + +void platformUartRestore(void) +{ + PlatSocketDeInit(); +} + +void PlatSocketDeInit(void) +{ + qorvoPlatUnRegisterPollFunction(PlatServerSocketId); + close(PlatServerSocketId); + qorvoPlatUnRegisterPollFunction(PlatSocketPipeFd[0]); + close(PlatSocketPipeFd[0]); + close(PlatSocketPipeFd[1]); + close(PlatSocketConnection.socketId); +} + +int PlatSocketTxData(uint16_t length, uint8_t *pData, uint32_t socketId) +{ + int result = -1; + + //All sockets + if (PlatSocketConnection.isValid) + { + if (PlatSocketConnection.socketId == socketId) + { + if (SOCKET_WRITE(PlatSocketConnection, (const char *)pData, length) < 0) + { + perror("TxSocket: Error Writing to client"); + close(PlatSocketConnection.socketId); + PlatSocketConnection.isValid = 0; + } + else + { + result = 0; + } + } + } + + return result; +} + diff --git a/examples/platforms/gp712/uart_qorvo.h b/examples/platforms/gp712/uart_qorvo.h new file mode 100644 index 000000000..edd577556 --- /dev/null +++ b/examples/platforms/gp712/uart_qorvo.h @@ -0,0 +1,89 @@ +/* + * 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 the declarations of the uart functions from the Qorvo library. + * + */ + +#ifndef UART_QORVO_H_ +#define UART_QORVO_H_ + +#include +#include + +/** + * This function initializes the UART driver. + * + */ +void qorvoUartInit(void); + +/** + * This function performs UART driver processing. + * + */ +void qorvoUartProcess(void); + +/** + * This function enables the UART driver. + * + */ +void qorvoUartInit(void); + +/** + * This function disables the UART driver. + * + */ +void qorvoUartDeInit(void); + +/** + * Callback function which will be called when uart transmission is done. + * + */ +void cbQorvoUartTxDone(void); + +/** + * Callback function which will be called when uart data is received. + * + * @param[in] aBuf A pointer to an array of received bytes. + * @param[in] aBufLength The number of bytes received from the uart. + * + */ +void qorvoUartSendInput(uint8_t* aBuf, uint16_t aBufLength); + +/** + * Function which transmits data via the uart. + * + * @param[out] aBuf A pointer to an array of bytes which need to be transmitted. + * @param[in] aBufLength The number of bytes to be transmitted via the uart. + * + */ +void qorvoUartSendOutput(const uint8_t *aBuf, uint16_t aBufLength); + +#endif // UART_QORVO_H_ diff --git a/third_party/Qorvo/LICENSE.txt b/third_party/Qorvo/LICENSE.txt new file mode 100644 index 000000000..555061fae --- /dev/null +++ b/third_party/Qorvo/LICENSE.txt @@ -0,0 +1,21 @@ + +Copyright (c) 2012-2016, GreenPeak Technologies +Copyright (c) 2017, Qorvo Inc + +This software is owned by Qorvo Inc +and protected under applicable copyright laws. +It is delivered under the terms of the license +and is intended and supplied for use solely and +exclusively with products manufactured by +Qorvo Inc. + +THIS SOFTWARE IS PROVIDED IN AN "AS IS" +CONDITION. NO WARRANTIES, WHETHER EXPRESS, +IMPLIED OR STATUTORY, INCLUDING, BUT NOT +LIMITED TO, IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. +QORVO INC. SHALL NOT, IN ANY +CIRCUMSTANCES, BE LIABLE FOR SPECIAL, +INCIDENTAL OR CONSEQUENTIAL DAMAGES, +FOR ANY REASON WHATSOEVER. diff --git a/third_party/Qorvo/README.md b/third_party/Qorvo/README.md new file mode 100644 index 000000000..638390fea --- /dev/null +++ b/third_party/Qorvo/README.md @@ -0,0 +1,13 @@ +# Qorvo + +## Version + +0.8.0.0 + +## License File + +[LICENSE](LICENSE.txt) + +## Description + +This library contains the glue code to interface with the kernel drivers for linux based platforms containing the gp712. \ No newline at end of file diff --git a/third_party/Qorvo/gp712/libQorvoRPi.a b/third_party/Qorvo/gp712/libQorvoRPi.a new file mode 100644 index 0000000000000000000000000000000000000000..c87a3c863c9a771b9d38dfa3d385f31d355e4876 GIT binary patch literal 316682 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 literal 0 HcmV?d00001