diff --git a/Makefile.am b/Makefile.am index 280c8443c..a4a44c9e2 100644 --- a/Makefile.am +++ b/Makefile.am @@ -33,6 +33,7 @@ AM_MAKEFLAGS = --no-print-directory AM_DISTCHECK_CONFIGURE_FLAGS = \ --enable-cli \ --enable-ncp \ + --enable-diag \ --with-examples=posix \ $(NULL) diff --git a/configure.ac b/configure.ac index 1a402cb51..5c8319c1e 100644 --- a/configure.ac +++ b/configure.ac @@ -428,6 +428,37 @@ AM_CONDITIONAL([OPENTHREAD_ENABLE_NCP], [test "${enable_ncp}" != "no"]) AM_CONDITIONAL([OPENTHREAD_ENABLE_NCP_SPI], [test "${enable_ncp}" = "spi"]) AM_CONDITIONAL([OPENTHREAD_ENABLE_NCP_UART], [test "${enable_ncp}" = "uart"]) +# +# Diagnostics Library +# + +AC_ARG_ENABLE(diag, + [AS_HELP_STRING([--enable-diag],[Enable diagnostics suport @<:@default=no@:>@.])], + [ + case "${enableval}" in + + no|yes) + enable_diag=${enableval} + ;; + + *) + AC_MSG_ERROR([Invalid value ${enable_diag} for --enable-diag]) + ;; + esac + ], + [enable_diag=no]) + +if test "$enable_diag" = "yes"; then + OPENTHREAD_ENABLE_DIAG=1 +else + OPENTHREAD_ENABLE_DIAG=0 +fi + +AC_MSG_RESULT(${enable_diag}) +AC_SUBST(OPENTHREAD_ENABLE_DIAG) +AM_CONDITIONAL([OPENTHREAD_ENABLE_DIAG], [test "${enable_diag}" = "yes"]) +AC_DEFINE_UNQUOTED([OPENTHREAD_ENABLE_DIAG],[${OPENTHREAD_ENABLE_DIAG}],[Define to 1 if you want to use diagnostics module]) + # # Examples # @@ -596,6 +627,7 @@ src/Makefile src/cli/Makefile src/ncp/Makefile src/core/Makefile +src/diag/Makefile third_party/Makefile third_party/mbedtls/Makefile examples/Makefile @@ -670,6 +702,7 @@ AC_MSG_NOTICE([ Pretty check args : ${PRETTY_CHECK_ARGS:--} OpenThread CLI support : ${enable_cli} OpenThread NCP support : ${enable_ncp} + OpenThread Diagnostics support : ${enable_diag} OpenThread examples : ${OPENTHREAD_EXAMPLES} OpenThread platform information : ${PLATFORM_INFO} diff --git a/examples/Makefile-cc2538 b/examples/Makefile-cc2538 index 04b06f84c..b0d78b540 100644 --- a/examples/Makefile-cc2538 +++ b/examples/Makefile-cc2538 @@ -44,6 +44,7 @@ BuildJobs ?= 99 configure_OPTIONS = \ --enable-cli \ --enable-ncp \ + --enable-diag \ --with-examples=cc2538 \ --with-platform-info=CC2538 \ $(NULL) diff --git a/examples/Makefile-posix b/examples/Makefile-posix index 2f5acec79..42eac0f13 100644 --- a/examples/Makefile-posix +++ b/examples/Makefile-posix @@ -89,6 +89,7 @@ TargetTuple = $(shell ${AbsTopSourceDir}/third_party/nlbuild configure_OPTIONS = \ --enable-cli \ --enable-ncp \ + --enable-diag \ --with-examples=posix \ --with-platform-info=POSIX \ $(NULL) diff --git a/examples/apps/cli/Makefile.am b/examples/apps/cli/Makefile.am index 2c810840b..6003f4211 100644 --- a/examples/apps/cli/Makefile.am +++ b/examples/apps/cli/Makefile.am @@ -46,6 +46,12 @@ ot_cli_LDADD = \ ot_cli_LDFLAGS = \ $(NULL) +if OPENTHREAD_ENABLE_DIAG +ot_cli_LDADD += \ + $(top_builddir)/src/diag/libopenthread-diag.a \ + $(NULL) +endif + if OPENTHREAD_EXAMPLES_POSIX ot_cli_LDADD += \ $(top_builddir)/examples/platforms/posix/libopenthread-posix.a \ diff --git a/examples/apps/cli/main.c b/examples/apps/cli/main.c index 63647287b..9afdaf7db 100644 --- a/examples/apps/cli/main.c +++ b/examples/apps/cli/main.c @@ -27,8 +27,10 @@ */ #include +#include +#include #include -#include +#include void otSignalTaskletPending(void) { @@ -40,6 +42,10 @@ int main(int argc, char *argv[]) otEnable(); otCliUartInit(); +#if OPENTHREAD_ENABLE_DIAG + diagInit(); +#endif + while (1) { otProcessNextTasklet(); diff --git a/examples/apps/ncp/Makefile.am b/examples/apps/ncp/Makefile.am index b131d0e5a..117d5a8a0 100644 --- a/examples/apps/ncp/Makefile.am +++ b/examples/apps/ncp/Makefile.am @@ -46,6 +46,12 @@ ot_ncp_LDADD = \ ot_ncp_LDFLAGS = \ $(NULL) +if OPENTHREAD_ENABLE_DIAG +ot_ncp_LDADD += \ + $(top_builddir)/src/diag/libopenthread-diag.a \ + $(NULL) +endif + if OPENTHREAD_EXAMPLES_POSIX ot_ncp_LDADD += \ $(top_builddir)/examples/platforms/posix/libopenthread-posix.a \ diff --git a/examples/apps/ncp/main.c b/examples/apps/ncp/main.c index cc7b1d884..2bfad09af 100644 --- a/examples/apps/ncp/main.c +++ b/examples/apps/ncp/main.c @@ -27,8 +27,10 @@ */ #include +#include +#include #include -#include +#include void otSignalTaskletPending(void) { @@ -40,6 +42,10 @@ int main(int argc, char *argv[]) otEnable(); otNcpInit(); +#if OPENTHREAD_ENABLE_DIAG + diagInit(); +#endif + while (1) { otProcessNextTasklet(); diff --git a/examples/platforms/Makefile.am b/examples/platforms/Makefile.am index 8c8ef753b..9dab26be2 100644 --- a/examples/platforms/Makefile.am +++ b/examples/platforms/Makefile.am @@ -45,10 +45,6 @@ if OPENTHREAD_EXAMPLES_CC2538 SUBDIRS = cc2538 endif -noinst_HEADERS = \ - platform.h \ - $(NULL) - # Always pretty (e.g. for 'make pretty') these subdirectories. PRETTY_SUBDIRS = \ diff --git a/examples/platforms/cc2538/Makefile.am b/examples/platforms/cc2538/Makefile.am index 5f9efac62..95e9aba47 100644 --- a/examples/platforms/cc2538/Makefile.am +++ b/examples/platforms/cc2538/Makefile.am @@ -46,6 +46,12 @@ libopenthread_cc2538_a_SOURCES = \ startup-gcc.c \ $(NULL) +if OPENTHREAD_ENABLE_DIAG +libopenthread_cc2538_a_SOURCES += \ + diag.c \ + $(NULL) +endif + noinst_HEADERS = \ cc2538-reg.h \ platform-cc2538.h \ diff --git a/examples/platforms/cc2538/alarm.c b/examples/platforms/cc2538/alarm.c index acfc73a2d..eac712f98 100644 --- a/examples/platforms/cc2538/alarm.c +++ b/examples/platforms/cc2538/alarm.c @@ -35,7 +35,9 @@ #include #include +#include #include +#include #include "platform-cc2538.h" enum @@ -99,7 +101,18 @@ void cc2538AlarmProcess(void) if (fire) { sIsRunning = false; - otPlatAlarmFired(); + +#if OPENTHREAD_ENABLE_DIAG + + if (otPlatDiagModeGet()) + { + otPlatDiagAlarmFired(); + } + else +#endif + { + otPlatAlarmFired(); + } } } diff --git a/examples/platforms/cc2538/diag.c b/examples/platforms/cc2538/diag.c new file mode 100644 index 000000000..433959b0d --- /dev/null +++ b/examples/platforms/cc2538/diag.c @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2016, Nest Labs, Inc. + * 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 "platform-cc2538.h" + +/** + * diagnostics mode flag. + * + */ +static bool sDiagMode = false; + +void otPlatDiagProcess(int argc, char *argv[], char *aOutput) +{ + // add more plarform specific diagnostics features here + + sprintf(aOutput, "diag feature '%s' is not supported\r\n", argv[0]); + (void)argc; +} + +void otPlatDiagModeSet(bool aMode) +{ + sDiagMode = aMode; +} + +bool otPlatDiagModeGet() +{ + return sDiagMode; +} diff --git a/examples/platforms/cc2538/radio.c b/examples/platforms/cc2538/radio.c index 8b934bc27..fd5a62000 100644 --- a/examples/platforms/cc2538/radio.c +++ b/examples/platforms/cc2538/radio.c @@ -33,9 +33,11 @@ */ #include +#include #include #include +#include #include "platform-cc2538.h" enum @@ -371,7 +373,17 @@ void cc2538RadioProcess(void) if ((sState == kStateReceive) && (sReceiveFrame.mLength > 0)) { - otPlatRadioReceiveDone(&sReceiveFrame, sReceiveError); +#if OPENTHREAD_ENABLE_DIAG + + if (otPlatDiagModeGet()) + { + otPlatDiagRadioReceiveDone(&sReceiveFrame, sReceiveError); + } + else +#endif + { + otPlatRadioReceiveDone(&sReceiveFrame, sReceiveError); + } } if (sState == kStateTransmit) @@ -379,14 +391,36 @@ void cc2538RadioProcess(void) if (sTransmitError != kThreadError_None || (sTransmitFrame.mPsdu[0] & IEEE802154_ACK_REQUEST) == 0) { sState = kStateReceive; - otPlatRadioTransmitDone(false, sTransmitError); + +#if OPENTHREAD_ENABLE_DIAG + + if (otPlatDiagModeGet()) + { + otPlatDiagRadioTransmitDone(false, sTransmitError); + } + else +#endif + { + otPlatRadioTransmitDone(false, sTransmitError); + } } else if (sReceiveFrame.mLength == IEEE802154_ACK_LENGTH && (sReceiveFrame.mPsdu[0] & IEEE802154_FRAME_TYPE_MASK) == IEEE802154_FRAME_TYPE_ACK && (sReceiveFrame.mPsdu[IEEE802154_DSN_OFFSET] == sTransmitFrame.mPsdu[IEEE802154_DSN_OFFSET])) { sState = kStateReceive; - otPlatRadioTransmitDone((sReceiveFrame.mPsdu[0] & IEEE802154_FRAME_PENDING) != 0, sTransmitError); + +#if OPENTHREAD_ENABLE_DIAG + + if (otPlatDiagModeGet()) + { + otPlatDiagRadioTransmitDone((sReceiveFrame.mPsdu[0] & IEEE802154_FRAME_PENDING) != 0, sTransmitError); + } + else +#endif + { + otPlatRadioTransmitDone((sReceiveFrame.mPsdu[0] & IEEE802154_FRAME_PENDING) != 0, sTransmitError); + } } } diff --git a/examples/platforms/posix/Makefile.am b/examples/platforms/posix/Makefile.am index d2f49fc23..345c1a064 100644 --- a/examples/platforms/posix/Makefile.am +++ b/examples/platforms/posix/Makefile.am @@ -46,6 +46,12 @@ libopenthread_posix_a_SOURCES = \ uart.c \ $(NULL) +if OPENTHREAD_ENABLE_DIAG +libopenthread_posix_a_SOURCES += \ + diag.c \ + $(NULL) +endif + if OPENTHREAD_ENABLE_NCP_SPI libopenthread_posix_a_SOURCES += \ spi-stubs.c \ diff --git a/examples/platforms/posix/alarm.c b/examples/platforms/posix/alarm.c index 71f45b836..4caf0048b 100644 --- a/examples/platforms/posix/alarm.c +++ b/examples/platforms/posix/alarm.c @@ -31,7 +31,9 @@ #include #include +#include #include +#include #include "platform-posix.h" static bool s_is_running = false; @@ -106,7 +108,18 @@ void posixAlarmProcess(void) if (remaining <= 0) { s_is_running = false; - otPlatAlarmFired(); + +#if OPENTHREAD_ENABLE_DIAG + + if (otPlatDiagModeGet()) + { + otPlatDiagAlarmFired(); + } + else +#endif + { + otPlatAlarmFired(); + } } } } diff --git a/examples/platforms/posix/diag.c b/examples/platforms/posix/diag.c new file mode 100644 index 000000000..e1d247946 --- /dev/null +++ b/examples/platforms/posix/diag.c @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2016, Nest Labs, Inc. + * 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 "platform-posix.h" + +/** + * diagnostics mode flag. + * + */ +static bool sDiagMode = false; + +void otPlatDiagProcess(int argc, char *argv[], char *aOutput) +{ + // no more diagnostics features for Posix platform + sprintf(aOutput, "diag feature '%s' is not supported\r\n", argv[0]); + (void)argc; +} + +void otPlatDiagModeSet(bool aMode) +{ + sDiagMode = aMode; +} + +bool otPlatDiagModeGet() +{ + return sDiagMode; +} diff --git a/examples/platforms/posix/radio.c b/examples/platforms/posix/radio.c index 1110f9046..a57c9a9e8 100644 --- a/examples/platforms/posix/radio.c +++ b/examples/platforms/posix/radio.c @@ -39,7 +39,9 @@ #include #include +#include #include +#include #include #include "platform-posix.h" @@ -456,7 +458,18 @@ void radioReceive(void) { sState = kStateReceive; sAckWait = false; - otPlatRadioTransmitDone(isFramePending(sReceiveFrame.mPsdu), kThreadError_None); + +#if OPENTHREAD_ENABLE_DIAG + + if (otPlatDiagModeGet()) + { + otPlatDiagRadioTransmitDone(isFramePending(sReceiveFrame.mPsdu), kThreadError_None); + } + else +#endif + { + otPlatRadioTransmitDone(isFramePending(sReceiveFrame.mPsdu), kThreadError_None); + } } } else if (sState == kStateReceive && @@ -478,7 +491,18 @@ void radioSendMessage(void) if (!sAckWait) { sState = kStateReceive; - otPlatRadioTransmitDone(false, kThreadError_None); + +#if OPENTHREAD_ENABLE_DIAG + + if (otPlatDiagModeGet()) + { + otPlatDiagRadioTransmitDone(false, kThreadError_None); + } + else +#endif + { + otPlatRadioTransmitDone(false, kThreadError_None); + } } } @@ -609,5 +633,16 @@ void radioProcessFrame(void) exit: - otPlatRadioReceiveDone(error == kThreadError_None ? &sReceiveFrame : NULL, error); +#if OPENTHREAD_ENABLE_DIAG + + if (otPlatDiagModeGet()) + { + otPlatDiagRadioReceiveDone(error == kThreadError_None ? &sReceiveFrame : NULL, error); + } + else +#endif + { + otPlatRadioReceiveDone(error == kThreadError_None ? &sReceiveFrame : NULL, error); + } } + diff --git a/include/Makefile.am b/include/Makefile.am index 29406db7f..49fe532f5 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -57,6 +57,7 @@ PRETTY_SUBDIRS = \ include_HEADERS = \ openthread.h \ + openthread-diag.h \ openthread-types.h \ $(NULL) diff --git a/include/openthread-diag.h b/include/openthread-diag.h new file mode 100644 index 000000000..c99857d12 --- /dev/null +++ b/include/openthread-diag.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2016, Nest Labs, Inc. + * 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 top-level functions for the OpenThread diagnostics library. + */ + +#ifndef OPENTHREAD_DIAG_H_ +#define OPENTHREAD_DIAG_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +void diagInit(); + +char *diagProcessCmd(int argc, char *argv[]); + +bool isDiagEnabled(); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // OPENTHREAD_DIAG_H_ diff --git a/include/platform/Makefile.am b/include/platform/Makefile.am index f71d3dbad..68f08915e 100644 --- a/include/platform/Makefile.am +++ b/include/platform/Makefile.am @@ -30,8 +30,10 @@ include $(abs_top_nlbuild_autotools_dir)/automake/pre.am ot_platform_headers =\ alarm.h \ + diag.h \ misc.h \ logging.h \ + platform.h \ radio.h \ random.h \ uart.h \ diff --git a/include/platform/alarm.h b/include/platform/alarm.h index b61ef0bbd..05b13264d 100644 --- a/include/platform/alarm.h +++ b/include/platform/alarm.h @@ -77,6 +77,11 @@ uint32_t otPlatAlarmGetNow(void); */ extern void otPlatAlarmFired(void); +/** + * Signal diagnostics module that the alarm has fired. + */ +extern void otPlatDiagAlarmFired(void); + /** * @} * diff --git a/include/platform/diag.h b/include/platform/diag.h new file mode 100644 index 000000000..33627d52f --- /dev/null +++ b/include/platform/diag.h @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2016, Nest Labs, Inc. + * 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 diag interface. + * + */ + +#ifndef DIAG_H_ +#define DIAG_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup diag Diag + * @ingroup platform + * + * @brief + * This module includes the platform abstraction for diagnostics features. + * + * @{ + * + */ + +/** + * Process the platform specific diagnostics features. + * + * @param[in] argc The argument counter of diagnostics command line. + * @param[in] argv The argument vector of diagnostics command line. + * @param[out] aOutput The diagnostics execution result. + */ +void otPlatDiagProcess(int argc, char *argv[], char *aOutput); + +/** + * Set diagnostics mode. + */ +void otPlatDiagModeSet(bool aMode); + +/** + * Get diagnostics mode. + */ +bool otPlatDiagModeGet(void); + +#ifdef __cplusplus +} // end of extern "C" +#endif + +#endif diff --git a/examples/platforms/platform.h b/include/platform/platform.h similarity index 92% rename from examples/platforms/platform.h rename to include/platform/platform.h index a6fb7986f..159a4d6c3 100644 --- a/examples/platforms/platform.h +++ b/include/platform/platform.h @@ -29,12 +29,17 @@ /** * @file * @brief - * This file includes the posix platform-specific initializers. + * This file defines the platform-specific initializers. */ #ifndef PLATFORM_H_ #define PLATFORM_H_ +#ifdef __cplusplus +extern "C" { +#endif + + /** * This function performs all platform-specific initialization. * @@ -47,4 +52,8 @@ void PlatformInit(int argc, char *argv[]); */ void PlatformProcessDrivers(void); +#ifdef __cplusplus +} // end of extern "C" +#endif + #endif // PLATFORM_H_ diff --git a/include/platform/radio.h b/include/platform/radio.h index c195c95ae..0d769ad8f 100644 --- a/include/platform/radio.h +++ b/include/platform/radio.h @@ -298,6 +298,28 @@ bool otPlatRadioGetPromiscuous(void); */ void otPlatRadioSetPromiscuous(bool aEnable); +/** + * The radio driver calls this method to notify OpenThread diagnostics module that the transmission has completed. + * + * @param[in] aFramePending TRUE if an ACK frame was received and the Frame Pending bit was set. + * @param[in] aError ::kThreadError_None when the frame was transmitted, ::kThreadError_NoAck when the frame was + * transmitted but no ACK was received, ::kThreadError_ChannelAccessFailure when the transmission + * could not take place due to activity on the channel, ::kThreadError_Abort when transmission was + * aborted for other reasons. + * + */ +extern void otPlatDiagRadioTransmitDone(bool aFramePending, ThreadError aError); + +/** + * The radio driver calls this method to notify OpenThread diagnostics module of a received packet. + * + * @param[in] aPacket A pointer to the received packet or NULL if the receive operation was aborted. + * @param[in] aError ::kThreadError_None when successfully received a frame, ::kThreadError_Abort when reception + * was aborted and a frame was not received. + * + */ +extern void otPlatDiagRadioReceiveDone(RadioPacket *aPacket, ThreadError aError); + /** * @} * diff --git a/src/Makefile.am b/src/Makefile.am index bdd315604..08627fcca 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -34,6 +34,7 @@ DIST_SUBDIRS = \ cli \ core \ ncp \ + diag \ $(NULL) # Always build (e.g. for 'make all') these subdirectories. @@ -50,6 +51,10 @@ if OPENTHREAD_ENABLE_NCP SUBDIRS += ncp endif +if OPENTHREAD_ENABLE_DIAG +SUBDIRS += diag +endif + # Always pretty (e.g. for 'make pretty') these subdirectories. PRETTY_SUBDIRS = \ diff --git a/src/cli/README.md b/src/cli/README.md index 786e19c68..3d9379f80 100644 --- a/src/cli/README.md +++ b/src/cli/README.md @@ -38,6 +38,7 @@ OpenThread test scripts use the CLI to execute test cases. * [thread](#thread) * [version](#version) * [whitelist](#whitelist) +* [diag](#diag) ## OpenThread Command Details @@ -680,3 +681,11 @@ Remove an IEEE 802.15.4 Extended Address from the whitelist. > whitelist remove dead00beef00cafe Done ``` + +### diag + +Diagnostics module is enabled only when building OpenThread with --enable-diag option. +Go [diagnostics module][1] for more information. + +[1]:../diag/README.md + diff --git a/src/cli/cli.cpp b/src/cli/cli.cpp index 1603b2d77..d43647c3c 100644 --- a/src/cli/cli.cpp +++ b/src/cli/cli.cpp @@ -36,7 +36,7 @@ #include #include -#include +#include #include "cli.hpp" #include "cli_dataset.hpp" @@ -86,6 +86,9 @@ const struct Command Interpreter::sCommands[] = { "thread", &ProcessThread }, { "version", &ProcessVersion }, { "whitelist", &ProcessWhitelist }, +#if OPENTHREAD_ENABLE_DIAG + { "diag", &ProcessDiag }, +#endif }; otNetifAddress Interpreter::sAddress; @@ -1477,6 +1480,14 @@ exit: AppendResult(error); } +#if OPENTHREAD_ENABLE_DIAG +void Interpreter::ProcessDiag(int argc, char *argv[]) +{ + // all diagnostics related features are processed within diagnostics module + sServer->OutputFormat("%s\r\n", diagProcessCmd(argc, argv)); +} +#endif + void Interpreter::ProcessLine(char *aBuf, uint16_t aBufLength, Server &aServer) { char *argv[kMaxArgs]; @@ -1504,6 +1515,11 @@ void Interpreter::ProcessLine(char *aBuf, uint16_t aBufLength, Server &aServer) cmd = aBuf; +#if OPENTHREAD_ENABLE_DIAG + VerifyOrExit((!isDiagEnabled() || (strcmp(cmd, "diag") == 0)), + sServer->OutputFormat("under diagnostics mode, execute 'diag stop' before running any other commands.\r\n")); +#endif + for (unsigned int i = 0; i < sizeof(sCommands) / sizeof(sCommands[0]); i++) { if (strcmp(cmd, sCommands[i].mName) == 0) diff --git a/src/cli/cli.hpp b/src/cli/cli.hpp index dc1035060..2cb638926 100644 --- a/src/cli/cli.hpp +++ b/src/cli/cli.hpp @@ -39,6 +39,7 @@ #include #include #include +#include namespace Thread { @@ -157,6 +158,10 @@ private: static void ProcessVersion(int argc, char *argv[]); static void ProcessWhitelist(int argc, char *argv[]); +#if OPENTHREAD_ENABLE_DIAG + static void ProcessDiag(int argc, char *argv[]); +#endif + static void HandleEchoResponse(void *aContext, Message &aMessage, const Ip6::MessageInfo &aMessageInfo); static void HandlePingTimer(void *aContext); static void HandleActiveScanResult(otActiveScanResult *aResult); diff --git a/src/diag/Makefile.am b/src/diag/Makefile.am new file mode 100644 index 000000000..173bea9fd --- /dev/null +++ b/src/diag/Makefile.am @@ -0,0 +1,53 @@ +# +# Copyright (c) 2016, Nest Labs, Inc. +# 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-diag.a + +libopenthread_diag_a_CPPFLAGS = \ + -I$(top_srcdir)/include \ + -I$(top_srcdir)/src \ + -I$(top_srcdir)/src/core \ + $(OPENTHREAD_TARGET_DEFINES) \ + $(NULL) + +libopenthread_diag_a_SOURCES = \ + openthread-diag.cpp \ + diag_process.cpp \ + $(NULL) + +noinst_HEADERS = \ + diag_process.hpp \ + $(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/diag/README.md b/src/diag/README.md new file mode 100644 index 000000000..29fbc56f7 --- /dev/null +++ b/src/diag/README.md @@ -0,0 +1,143 @@ +# OpenThread Diagnostics Module Reference + +The OpenThread diagnostics module is a tool for debugging platform hardware manually, and it will also be used during manufacturing process, to verify platform hardware performance. + +The diagnostics module supports common diagnostics features that are listed below, and it also provides a mechanism for expanding platform specific diagnostics features. + + +## Common Diagnostics Command List + +* [diag](#diag) +* [diag start](#diag start) +* [diag channel](#diag channel) +* [diag channel \](#diag channel \) +* [diag power](#diag power) +* [diag power \](#diag power \) +* [diag send \ \](#diag send \ \) +* [diag repeat \ \](#diag repeat \ \) +* [diag repeat stop](#diag repeat stop) +* [diag sleep](#diag sleep) +* [diag stats](#diag stats) +* [diag stop](#diag stop) + + +### diag + +Show diagnostics mode status. + +```bash +> diag +diagnostics mode is disabled +``` + +### diag start + +Start diagnostics mode. + +```bash +> diag start +start diagnostics mode +status 0x00 +``` + +### diag channel + +Get the IEEE 802.15.4 Channel value for diagnostics module. + +```bash +> diag channel +channel: 11 +``` + +### diag channel \ + +Set the IEEE 802.15.4 Channel value for diagnostics module. + +```bash +> diag channel 11 +set channel to 11 +status 0x00 +``` + +### diag power + +Get the tx power value(dBm) for diagnostics module. + +```bash +> diag power +tx power: -10 dBm +``` + +### diag power \ + +Set the tx power value(dBm) for diagnostics module. + +```bash +> diag power -10 +set tx power to -10 dBm +status 0x00 +``` + +### diag send \ \ + +Transmit a fixed number of packets with fixed length. + +```bash +> diag send 20 100 +sending 0x14 packet(s), length 0x64 +status 0x00 +``` + +### diag repeat \ \ + +Transmit packets repeatedly with a fixed interval. + +```bash +> diag repeat 100 100 +sending packets of length 0x64 at the delay of 0x64 ms +status 0x00 +``` + +### diag repeat stop + +Stop repeated packet transmission. + +```bash +> diag repeat stop +repeated packet transmission is stopped +status 0x00 +``` + +### diag sleep + +Enter radio sleep mode. + +```bash +> diag sleep +sleeping now... +``` + +### diag stats + +Print statistics during diagnostics mode. + +```bash +> diag stats +received packets: 10 +sent packets: 10 +first received packet: rssi=-65, lqi=101 +``` + +### diag stop + +Stop diagnostics mode and print statistics. + +```bash +> diag stop +received packets: 10 +sent packets: 10 +first received packet: rssi=-65, lqi=101 + +stop diagnostics mode +status 0x00 +``` diff --git a/src/diag/diag_process.cpp b/src/diag/diag_process.cpp new file mode 100644 index 000000000..bcdd8aedc --- /dev/null +++ b/src/diag/diag_process.cpp @@ -0,0 +1,383 @@ +/* + * Copyright (c) 2016, Nest Labs, Inc. + * 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 diagnostics module. + */ + +#include +#include +#include +#include + +#include "diag_process.hpp" + +namespace Thread { +namespace Diagnostics { + +const struct Command Diag::sCommands[] = +{ + { "start", &ProcessStart }, + { "stop", &ProcessStop }, + { "channel", &ProcessChannel }, + { "power", &ProcessPower }, + { "send", &ProcessSend }, + { "repeat", &ProcessRepeat }, + { "sleep", &ProcessSleep }, + { "stats", &ProcessStats }, +}; + +char Diag::sDiagOutput[MAX_DIAG_OUTPUT]; +struct DiagStats Diag::sStats; + +int8_t Diag::sTxPower; +uint8_t Diag::sChannel; +uint8_t Diag::sTxLen; +uint32_t Diag::sTxPeriod; +uint32_t Diag::sTxPackets; + +void Diag::Init() +{ + sChannel = 20; + sTxPower = 0; + sTxPeriod = 0; + sTxLen = 0; + sTxPackets = 0; + memset(&sStats, 0, sizeof(struct DiagStats)); +} + +char *Diag::ProcessCmd(int argc, char *argv[]) +{ + unsigned int i; + + if (argc == 0) + { + sprintf(sDiagOutput, "diagnostics mode is %s\r\n", otPlatDiagModeGet() ? "enabled" : "disabled"); + } + else + { + for (i = 0; i < sizeof(sCommands) / sizeof(sCommands[0]); i++) + { + if (strcmp(argv[0], sCommands[i].mName) == 0) + { + sCommands[i].mCommand(argc - 1, &argv[1], sDiagOutput); + break; + } + } + + // more platform specific features will be processed under platform layer + if (i == sizeof(sCommands) / sizeof(sCommands[0])) + { + otPlatDiagProcess(argc, argv, sDiagOutput); + } + } + + return sDiagOutput; +} + +bool Diag::isEnabled() +{ + return otPlatDiagModeGet(); +} + +void Diag::AppendErrorResult(ThreadError aError, char *aOutput) +{ + if (aError != kThreadError_None) + { + sprintf(aOutput, "failed\r\nstatus %#x\r\n", aError); + } +} + +void Diag::ProcessStart(int argc, char *argv[], char *aOutput) +{ + ThreadError error = kThreadError_None; + + // enable radio + otPlatRadioEnable(); + + // enable promiscuous mode + otPlatRadioSetPromiscuous(true); + + // stop timer + otPlatAlarmStop(); + + // start to listen on the default channel + SuccessOrExit(error = otPlatRadioReceive(sChannel)); + + // enable diagnostics mode + otPlatDiagModeSet(true); + + memset(&sStats, 0, sizeof(struct DiagStats)); + + sprintf(aOutput, "start diagnostics mode\r\nstatus 0x%02x\r\n", error); + +exit: + (void)argc; + (void)argv; + AppendErrorResult(error, aOutput); +} + +void Diag::ProcessStop(int argc, char *argv[], char *aOutput) +{ + ThreadError error = kThreadError_None; + + VerifyOrExit(otPlatDiagModeGet(), error = kThreadError_InvalidState); + + otPlatAlarmStop(); + otPlatDiagModeSet(false); + otPlatRadioSetPromiscuous(false); + + sprintf(aOutput, "received packets: %d\r\nsent packets: %d\r\nfirst received packet: rssi=%d, lqi=%d\r\n\nstop diagnostics mode\r\nstatus 0x%02x\r\n", + static_cast(sStats.received_packets), static_cast(sStats.sent_packets), static_cast(sStats.first_rssi), + static_cast(sStats.first_lqi), error); + +exit: + (void)argc; + (void)argv; + AppendErrorResult(error, aOutput); +} + +ThreadError Diag::ParseLong(char *argv, long &value) +{ + char *endptr; + value = strtol(argv, &endptr, 0); + return (*endptr == '\0') ? kThreadError_None : kThreadError_Parse; +} + +void Diag::TxPacket() +{ + RadioPacket *packet = otPlatRadioGetTransmitBuffer(); + + if (sTxPackets > 0) + { + sTxPackets--; + } + + packet->mLength = sTxLen; + packet->mChannel = sChannel; + packet->mPower = sTxPower; + + for (uint8_t i = 0; i < sTxLen; i++) + { + packet->mPsdu[i] = i; + } + + otPlatRadioTransmit(); +} + +void Diag::ProcessChannel(int argc, char *argv[], char *aOutput) +{ + ThreadError error = kThreadError_None; + + VerifyOrExit(otPlatDiagModeGet(), error = kThreadError_InvalidState); + + if (argc == 0) + { + sprintf(aOutput, "channel: %d\r\n", sChannel); + } + else + { + long value; + + SuccessOrExit(error = ParseLong(argv[0], value)); + VerifyOrExit(value >= kPhyMinChannel && value <= kPhyMaxChannel, error = kThreadError_InvalidArgs); + sChannel = static_cast(value); + + // listen on the set channel immediately + otPlatRadioReceive(sChannel); + sprintf(aOutput, "set channel to %d\r\nstatus 0x%02x\r\n", sChannel, error); + } + +exit: + AppendErrorResult(error, aOutput); +} + +void Diag::ProcessPower(int argc, char *argv[], char *aOutput) +{ + ThreadError error = kThreadError_None; + + VerifyOrExit(otPlatDiagModeGet(), error = kThreadError_InvalidState); + + if (argc == 0) + { + sprintf(aOutput, "tx power: %d dBm\r\n", sTxPower); + } + else + { + long value; + + SuccessOrExit(error = ParseLong(argv[0], value)); + sTxPower = static_cast(value); + sprintf(aOutput, "set tx power to %d dBm\r\nstatus 0x%02x\r\n", sTxPower, error); + } + +exit: + AppendErrorResult(error, aOutput); +} + +void Diag::ProcessSend(int argc, char *argv[], char *aOutput) +{ + ThreadError error = kThreadError_None; + long value; + + VerifyOrExit(otPlatDiagModeGet(), error = kThreadError_InvalidState); + VerifyOrExit(argc == 2, error = kThreadError_InvalidArgs); + + SuccessOrExit(error = ParseLong(argv[0], value)); + sTxPackets = static_cast(value); + + SuccessOrExit(error = ParseLong(argv[1], value)); + VerifyOrExit(value <= kMaxPHYPacketSize, error = kThreadError_InvalidArgs); + sTxLen = static_cast(value); + + sprintf(aOutput, "sending %#x packet(s), length %#x\r\nstatus 0x%02x\r\n", static_cast(sTxPackets), static_cast(sTxLen), error); + TxPacket(); + +exit: + AppendErrorResult(error, aOutput); +} + +void Diag::ProcessRepeat(int argc, char *argv[], char *aOutput) +{ + ThreadError error = kThreadError_None; + + VerifyOrExit(otPlatDiagModeGet(), error = kThreadError_InvalidState); + VerifyOrExit(argc > 0, error = kThreadError_InvalidArgs); + + if (strcmp(argv[0], "stop") == 0) + { + otPlatAlarmStop(); + sprintf(aOutput, "repeated packet transmission is stopped\r\nstatus 0x%02x\r\n", error); + } + else + { + long value; + + VerifyOrExit(argc == 2, error = kThreadError_InvalidArgs); + + SuccessOrExit(error = ParseLong(argv[0], value)); + sTxPeriod = static_cast(value); + + SuccessOrExit(error = ParseLong(argv[1], value)); + VerifyOrExit(value <= kMaxPHYPacketSize, error = kThreadError_InvalidArgs); + sTxLen = static_cast(value); + + uint32_t now = otPlatAlarmGetNow(); + otPlatAlarmStartAt(now, sTxPeriod); + sprintf(aOutput, "sending packets of length %#x at the delay of %#x ms\r\nstatus 0x%02x\r\n", static_cast(sTxLen), static_cast(sTxPeriod), error); + } + +exit: + AppendErrorResult(error, aOutput); +} + +void Diag::ProcessSleep(int argc, char *argv[], char *aOutput) +{ + ThreadError error = kThreadError_None; + + VerifyOrExit(otPlatDiagModeGet(), error = kThreadError_InvalidState); + + otPlatRadioSleep(); + sprintf(aOutput, "sleeping now...\r\n"); + +exit: + (void)argc; + (void)argv; + AppendErrorResult(error, aOutput); +} + +void Diag::ProcessStats(int argc, char *argv[], char *aOutput) +{ + ThreadError error = kThreadError_None; + + VerifyOrExit(otPlatDiagModeGet(), error = kThreadError_InvalidState); + + sprintf(aOutput, "received packets: %d\r\nsent packets: %d\r\nfirst received packet: rssi=%d, lqi=%d\r\n", + static_cast(sStats.received_packets), static_cast(sStats.sent_packets), + static_cast(sStats.first_rssi), static_cast(sStats.first_lqi)); + +exit: + (void)argc; + (void)argv; + AppendErrorResult(error, aOutput); +} + +void Diag::DiagTransmitDone(bool aRxPending, ThreadError aError) +{ + if (!aRxPending && aError == kThreadError_None) + { + sStats.sent_packets++; + + if (sTxPackets > 0) + { + TxPacket(); + } + } +} + +void Diag::DiagReceiveDone(RadioPacket *aFrame, ThreadError aError) +{ + if (aError == kThreadError_None) + { + // for sensitivity test, only record the rssi and lqi for the first packet + if (sStats.received_packets == 0) + { + sStats.first_rssi = aFrame->mPower; + sStats.first_lqi = aFrame->mLqi; + } + + sStats.received_packets++; + } +} + +void Diag::AlarmFired() +{ + uint32_t now = otPlatAlarmGetNow(); + + TxPacket(); + otPlatAlarmStartAt(now, sTxPeriod); +} + +extern "C" void otPlatDiagAlarmFired() +{ + Diag::AlarmFired(); +} + +extern "C" void otPlatDiagRadioTransmitDone(bool aRxPending, ThreadError aError) +{ + Diag::DiagTransmitDone(aRxPending, aError); +} + +extern "C" void otPlatDiagRadioReceiveDone(RadioPacket *aFrame, ThreadError aError) +{ + Diag::DiagReceiveDone(aFrame, aError); +} + +} // namespace Diagnostics +} // namespace Thread diff --git a/src/diag/diag_process.hpp b/src/diag/diag_process.hpp new file mode 100644 index 000000000..80d8c25b2 --- /dev/null +++ b/src/diag/diag_process.hpp @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2016, Nest Labs, Inc. + * 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 contains definitions for the diagnostics module. + */ + +#ifndef DIAG_PROCESS_HPP_ +#define DIAG_PROCESS_HPP_ + +#include +#include +#include +#include + +namespace Thread { + +namespace Diagnostics { + +#define MAX_DIAG_OUTPUT 256 + +struct Command +{ + const char *mName; ///< A pointer to the command string. + void (*mCommand)(int argc, char *argv[], char *aOutput); ///< A function pointer to process the command. +}; + +struct DiagStats +{ + uint32_t received_packets; + uint32_t sent_packets; + int8_t first_rssi; + uint8_t first_lqi; +}; + +class Diag +{ +public: + static void Init(); + static char *ProcessCmd(int argc, char *argv[]); + static bool isEnabled(); + + static void DiagTransmitDone(bool aRxPending, ThreadError aError); + static void DiagReceiveDone(RadioPacket *aFrame, ThreadError aError); + static void AlarmFired(); + +private: + static void AppendErrorResult(ThreadError error, char *aOutput); + static void ProcessSleep(int argc, char *argv[], char *aOutput); + static void ProcessStart(int argc, char *argv[], char *aOutput); + static void ProcessStop(int argc, char *argv[], char *aOutput); + static void ProcessSend(int argc, char *argv[], char *aOutput); + static void ProcessRepeat(int argc, char *argv[], char *aOutput); + static void ProcessStats(int argc, char *argv[], char *aOutput); + static void ProcessChannel(int argc, char *argv[], char *aOutput); + static void ProcessPower(int argc, char *argv[], char *aOutput); + static void TxPacket(); + static ThreadError ParseLong(char *aString, long &aLong); + + static char sDiagOutput[]; + static const struct Command sCommands[]; + static struct DiagStats sStats; + static int8_t sTxPower; + static uint8_t sChannel; + static uint8_t sTxLen; + static uint32_t sTxPeriod; + static uint32_t sTxPackets; +}; + +} // namespace Diagnostics +} // namespace Thread + +#endif // CLI_HPP_ diff --git a/src/diag/openthread-diag.cpp b/src/diag/openthread-diag.cpp new file mode 100644 index 000000000..3d221d1a0 --- /dev/null +++ b/src/diag/openthread-diag.cpp @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2016, Nest Labs, Inc. + * 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 top-level interface to diagnostics module. + */ + +#include +#include +#include + +#include +#include + +namespace Thread { + +namespace Diagnostics { + +#ifdef __cplusplus +extern "C" { +#endif + +void diagInit() +{ + Diag::Init(); +} + +char *diagProcessCmd(int argc, char *argv[]) +{ + return Diag::ProcessCmd(argc, argv); +} + +bool isDiagEnabled() +{ + return Diag::isEnabled(); +} + +#ifdef __cplusplus +} // extern "C" +#endif + +} // namespace Diagnostics +} // namespace Thread diff --git a/tests/unit/Makefile.am b/tests/unit/Makefile.am index efbcd9d55..b71d304b5 100644 --- a/tests/unit/Makefile.am +++ b/tests/unit/Makefile.am @@ -75,6 +75,12 @@ check_PROGRAMS = \ test-toolchain \ $(NULL) +if OPENTHREAD_ENABLE_DIAG +check_PROGRAMS += \ + test-diag \ + $(NULL) +endif + if OPENTHREAD_ENABLE_NCP COMMON_LDADD += \ @@ -129,6 +135,13 @@ test_timer_SOURCES = test_timer.cpp test_toolchain_LDADD = $(COMMON_LDADD) test_toolchain_SOURCES = test_toolchain.cpp +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_SOURCES = test_diag.cpp +endif + endif # OPENTHREAD_BUILD_TESTS include $(abs_top_nlbuild_autotools_dir)/automake/post.am diff --git a/tests/unit/test_diag.cpp b/tests/unit/test_diag.cpp new file mode 100644 index 000000000..488c773af --- /dev/null +++ b/tests/unit/test_diag.cpp @@ -0,0 +1,191 @@ +/* + * Copyright (c) 2016, Nest Labs, Inc. + * 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 "test_util.h" +#include +#include +#include +#include + +extern "C" void otSignalTaskletPending(void) +{ +} + +extern "C" bool otAreTaskletsPending(void) +{ + return false; +} + +extern "C" void otPlatUartSendDone(void) +{ +} + +extern "C" void otPlatUartReceived(const uint8_t *aBuf, uint16_t aBufLength) +{ + (void)aBuf; + (void)aBufLength; +} + +extern "C" void otPlatAlarmFired() +{ +} + +extern "C" void otPlatRadioTransmitDone(bool aRxPending, ThreadError aError) +{ + (void)aRxPending; + (void)aError; +} + +extern "C" void otPlatRadioReceiveDone(RadioPacket *aFrame, ThreadError aError) +{ + (void)aFrame; + (void)aError; +} + + +/** + * diagnostics module tests + */ +void TestDiag() +{ + static const struct + { + const char *command; + const char *output; + } tests[] = + { + { + "diag\n", + "diagnostics mode is disabled\r\n", + }, + { + "diag send 10 100\n", + "failed\r\nstatus 0xe\r\n", + }, + { + "diag start\n", + "start diagnostics mode\r\nstatus 0x00\r\n", + }, + { + "diag\n", + "diagnostics mode is enabled\r\n", + }, + { + "diag channel 10\n", + "failed\r\nstatus 0x7\r\n", + }, + { + "diag channel 11\n", + "set channel to 11\r\nstatus 0x00\r\n", + }, + { + "diag channel\n", + "channel: 11\r\n", + }, + { + "diag power -10\n", + "set tx power to -10 dBm\r\nstatus 0x00\r\n", + }, + { + "diag power\n", + "tx power: -10 dBm\r\n", + }, + { + "diag stats\n", + "received packets: 0\r\nsent packets: 0\r\nfirst received packet: rssi=0, lqi=0\r\n", + }, + { + "diag send 20 100\n", + "sending 0x14 packet(s), length 0x64\r\nstatus 0x00\r\n", + }, + { + "diag repeat 100 100\n", + "sending packets of length 0x64 at the delay of 0x64 ms\r\nstatus 0x00\r\n" + }, + { + "diag sleep\n", + "sleeping now...\r\n", + }, + { + "diag stop\n", + "received packets: 0\r\nsent packets: 0\r\nfirst received packet: rssi=0, lqi=0\r\n\nstop diagnostics mode\r\nstatus 0x00\r\n", + }, + { + "diag\n", + "diagnostics mode is disabled\r\n", + }, + }; + + // initialize platform layer + int argc = 2; + char *argv[2] = {(char *)"test_diag", (char *)"1"}; + PlatformInit(argc, argv); + + // initialize diagnostics module + diagInit(); + + // test diagnostics commands + VerifyOrQuit(!isDiagEnabled(), "diagnostics mode shoud be disabled as default\n"); + + for (unsigned int i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) + { + char string[50]; + int length = strlen(tests[i].command); + + char *cmd; + char *argv[8]; + int argc = 0; + char *output = NULL; + + memcpy(string, tests[i].command, length + 1); + + for (cmd = string + 1; (cmd < string + length) && (cmd != NULL); ++cmd) + { + if (*cmd == ' ' || *cmd == '\r' || *cmd == '\n') + { + *cmd = '\0'; + } + + if (*(cmd - 1) == '\0' && *cmd != ' ') + { + argv[argc++] = cmd; + } + } + + output = diagProcessCmd(argc, argv); + VerifyOrQuit(memcmp(output, tests[i].output, strlen(tests[i].output)) == 0, + "Test Diagnostics module failed\r\n"); + } +} + +int main(void) +{ + TestDiag(); + printf("All tests passed\n"); + return 0; +} diff --git a/tests/unit/test_lowpan.cpp b/tests/unit/test_lowpan.cpp index e6c0c6f12..7a03fcccd 100644 --- a/tests/unit/test_lowpan.cpp +++ b/tests/unit/test_lowpan.cpp @@ -59,6 +59,22 @@ extern "C" void otPlatUartReceived(const uint8_t *aBuf, uint16_t aBufLength) (void)aBufLength; } +extern "C" void otPlatDiagAlarmFired() +{ +} + +extern "C" void otPlatDiagRadioTransmitDone(bool aRxPending, ThreadError aError) +{ + (void)aRxPending; + (void)aError; +} + +extern "C" void otPlatDiagRadioReceiveDone(RadioPacket *aFrame, ThreadError aError) +{ + (void)aFrame; + (void)aError; +} + ThreadNetif sMockThreadNetif; Lowpan::Lowpan sMockLowpan(sMockThreadNetif); diff --git a/tests/unit/test_message.cpp b/tests/unit/test_message.cpp index d58006b5f..09c1f1549 100644 --- a/tests/unit/test_message.cpp +++ b/tests/unit/test_message.cpp @@ -36,6 +36,10 @@ extern"C" void otSignalTaskletPending(void) { } +extern "C" void otPlatDiagAlarmFired() +{ +} + void TestMessage(void) { Thread::Message *message; diff --git a/tests/unit/test_ncp_buffer.cpp b/tests/unit/test_ncp_buffer.cpp index b580f8f17..b7db16527 100644 --- a/tests/unit/test_ncp_buffer.cpp +++ b/tests/unit/test_ncp_buffer.cpp @@ -40,6 +40,10 @@ extern"C" void otSignalTaskletPending(void) { } +extern "C" void otPlatDiagAlarmFired() +{ +} + enum { kTestBufferSize = 101, // Size of backed buffer for NcpFrameBuffer.