blob: d77d114e573845ed372adeae3ca0ac6da7206a22 [file] [log] [blame]
/*
* Copyright (c) 2018, The OpenThread Authors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include "platform/openthread-posix-config.h"
#include <openthread/config.h>
#include <assert.h>
#include <errno.h>
#include <getopt.h>
#include <libgen.h>
#include <setjmp.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <unistd.h>
#ifdef __linux__
#include <sys/prctl.h>
#endif
#ifndef HAVE_LIBEDIT
#define HAVE_LIBEDIT 0
#endif
#ifndef HAVE_LIBREADLINE
#define HAVE_LIBREADLINE 0
#endif
#define OT_POSIX_APP_TYPE_NCP 1
#define OT_POSIX_APP_TYPE_CLI 2
#include <openthread/diag.h>
#include <openthread/logging.h>
#include <openthread/tasklet.h>
#include <openthread/thread.h>
#include <openthread/platform/radio.h>
#if OPENTHREAD_POSIX_APP_TYPE == OT_POSIX_APP_TYPE_NCP
#include <openthread/ncp.h>
#define OPENTHREAD_USE_CONSOLE 0
#elif OPENTHREAD_POSIX_APP_TYPE == OT_POSIX_APP_TYPE_CLI
#include <openthread/cli.h>
#if (HAVE_LIBEDIT || HAVE_LIBREADLINE) && !OPENTHREAD_POSIX_CONFIG_DAEMON_ENABLE
#define OPENTHREAD_USE_CONSOLE 1
#include "console_cli.h"
#else
#define OPENTHREAD_USE_CONSOLE 0
#endif
#else
#error "Unknown posix app type!"
#endif
#include <common/code_utils.hpp>
#include <common/logging.hpp>
#include <lib/platform/exit_code.h>
#include <openthread/openthread-system.h>
#ifndef OPENTHREAD_ENABLE_COVERAGE
#define OPENTHREAD_ENABLE_COVERAGE 0
#endif
typedef struct PosixConfig
{
otPlatformConfig mPlatformConfig; ///< Platform configuration.
otLogLevel mLogLevel; ///< Debug level of logging.
bool mIsDryRun; ///< Dry run.
bool mPrintRadioVersion; ///< Whether to print radio firmware version.
bool mIsVerbose; ///< Whether to print log to stderr.
} PosixConfig;
static jmp_buf gResetJump;
void __gcov_flush();
/**
* This enumeration defines the argument return values.
*
*/
enum
{
OT_POSIX_OPT_DEBUG_LEVEL = 'd',
OT_POSIX_OPT_DRY_RUN = 'n',
OT_POSIX_OPT_HELP = 'h',
OT_POSIX_OPT_INTERFACE_NAME = 'I',
OT_POSIX_OPT_TIME_SPEED = 's',
OT_POSIX_OPT_VERBOSE = 'v',
OT_POSIX_OPT_SHORT_MAX = 128,
OT_POSIX_OPT_RADIO_VERSION,
OT_POSIX_OPT_REAL_TIME_SIGNAL,
};
static const struct option kOptions[] = {{"debug-level", required_argument, NULL, OT_POSIX_OPT_DEBUG_LEVEL},
{"dry-run", no_argument, NULL, OT_POSIX_OPT_DRY_RUN},
{"help", no_argument, NULL, OT_POSIX_OPT_HELP},
{"interface-name", required_argument, NULL, OT_POSIX_OPT_INTERFACE_NAME},
{"radio-version", no_argument, NULL, OT_POSIX_OPT_RADIO_VERSION},
{"real-time-signal", required_argument, NULL, OT_POSIX_OPT_REAL_TIME_SIGNAL},
{"time-speed", required_argument, NULL, OT_POSIX_OPT_TIME_SPEED},
{"verbose", no_argument, NULL, OT_POSIX_OPT_VERBOSE},
{0, 0, 0, 0}};
static void PrintUsage(const char *aProgramName, FILE *aStream, int aExitCode)
{
fprintf(aStream,
"Syntax:\n"
" %s [Options] RadioURL\n"
"Options:\n"
" -d --debug-level Debug level of logging.\n"
" -h --help Display this usage information.\n"
" -I --interface-name name Thread network interface name.\n"
" -n --dry-run Just verify if arguments is valid and radio spinel is compatible.\n"
" --radio-version Print radio firmware version.\n"
" -s --time-speed factor Time speed up factor.\n"
" -v --verbose Also log to stderr.\n",
aProgramName);
#ifdef __linux__
fprintf(aStream,
" --real-time-signal (Linux only) The real-time signal number for microsecond timer.\n"
" Use +N for relative value to SIGRTMIN, and use N for absolute value.\n");
#endif
fprintf(aStream, "%s", otSysGetRadioUrlHelpString());
exit(aExitCode);
}
static void ParseArg(int aArgCount, char *aArgVector[], PosixConfig *aConfig)
{
memset(aConfig, 0, sizeof(*aConfig));
aConfig->mPlatformConfig.mSpeedUpFactor = 1;
aConfig->mLogLevel = OT_LOG_LEVEL_CRIT;
#ifdef __linux__
aConfig->mPlatformConfig.mRealTimeSignal = SIGRTMIN;
#endif
optind = 1;
while (true)
{
int index = 0;
int option = getopt_long(aArgCount, aArgVector, "d:hI:ns:v", kOptions, &index);
if (option == -1)
{
break;
}
switch (option)
{
case OT_POSIX_OPT_DEBUG_LEVEL:
aConfig->mLogLevel = (otLogLevel)atoi(optarg);
break;
case OT_POSIX_OPT_HELP:
PrintUsage(aArgVector[0], stdout, OT_EXIT_SUCCESS);
break;
case OT_POSIX_OPT_INTERFACE_NAME:
aConfig->mPlatformConfig.mInterfaceName = optarg;
break;
case OT_POSIX_OPT_DRY_RUN:
aConfig->mIsDryRun = true;
break;
case OT_POSIX_OPT_TIME_SPEED:
{
char *endptr = NULL;
aConfig->mPlatformConfig.mSpeedUpFactor = (uint32_t)strtol(optarg, &endptr, 0);
if (*endptr != '\0' || aConfig->mPlatformConfig.mSpeedUpFactor == 0)
{
fprintf(stderr, "Invalid value for TimerSpeedUpFactor: %s\n", optarg);
exit(OT_EXIT_INVALID_ARGUMENTS);
}
break;
}
case OT_POSIX_OPT_VERBOSE:
aConfig->mIsVerbose = true;
break;
case OT_POSIX_OPT_RADIO_VERSION:
aConfig->mPrintRadioVersion = true;
break;
#ifdef __linux__
case OT_POSIX_OPT_REAL_TIME_SIGNAL:
if (optarg[0] == '+')
{
aConfig->mPlatformConfig.mRealTimeSignal = SIGRTMIN + atoi(&optarg[1]);
}
else
{
aConfig->mPlatformConfig.mRealTimeSignal = atoi(optarg);
}
break;
#endif // __linux__
case '?':
PrintUsage(aArgVector[0], stderr, OT_EXIT_INVALID_ARGUMENTS);
break;
default:
assert(false);
break;
}
}
if (optind >= aArgCount)
{
PrintUsage(aArgVector[0], stderr, OT_EXIT_INVALID_ARGUMENTS);
}
aConfig->mPlatformConfig.mRadioUrl = aArgVector[optind];
}
static otInstance *InitInstance(int aArgCount, char *aArgVector[])
{
PosixConfig config;
otInstance *instance = NULL;
ParseArg(aArgCount, aArgVector, &config);
openlog(aArgVector[0], LOG_PID | (config.mIsVerbose ? LOG_PERROR : 0), LOG_DAEMON);
setlogmask(setlogmask(0) & LOG_UPTO(LOG_DEBUG));
syslog(LOG_INFO, "Running %s", otGetVersionString());
syslog(LOG_INFO, "Thread version: %hu", otThreadGetVersion());
IgnoreError(otLoggingSetLevel(config.mLogLevel));
instance = otSysInit(&config.mPlatformConfig);
if (config.mPrintRadioVersion)
{
printf("%s\n", otPlatRadioGetVersionString(instance));
}
else
{
syslog(LOG_INFO, "RCP version: %s", otPlatRadioGetVersionString(instance));
}
if (config.mIsDryRun)
{
exit(OT_EXIT_SUCCESS);
}
return instance;
}
void otTaskletsSignalPending(otInstance *aInstance)
{
OT_UNUSED_VARIABLE(aInstance);
}
void otPlatReset(otInstance *aInstance)
{
otInstanceFinalize(aInstance);
otSysDeinit();
longjmp(gResetJump, 1);
assert(false);
}
int main(int argc, char *argv[])
{
otInstance *instance;
#ifdef __linux__
// Ensure we terminate this process if the
// parent process dies.
prctl(PR_SET_PDEATHSIG, SIGHUP);
#endif
if (setjmp(gResetJump))
{
alarm(0);
#if OPENTHREAD_ENABLE_COVERAGE
__gcov_flush();
#endif
execvp(argv[0], argv);
}
instance = InitInstance(argc, argv);
#if OPENTHREAD_POSIX_APP_TYPE == OT_POSIX_APP_TYPE_NCP
otNcpInit(instance);
#elif OPENTHREAD_POSIX_APP_TYPE == OT_POSIX_APP_TYPE_CLI
#if OPENTHREAD_USE_CONSOLE
otxConsoleInit(instance);
#else
otCliUartInit(instance);
#endif
#endif
while (true)
{
otSysMainloopContext mainloop;
otTaskletsProcess(instance);
FD_ZERO(&mainloop.mReadFdSet);
FD_ZERO(&mainloop.mWriteFdSet);
FD_ZERO(&mainloop.mErrorFdSet);
mainloop.mMaxFd = -1;
mainloop.mTimeout.tv_sec = 10;
mainloop.mTimeout.tv_usec = 0;
#if OPENTHREAD_USE_CONSOLE
otxConsoleUpdate(&mainloop);
#endif
otSysMainloopUpdate(instance, &mainloop);
if (otSysMainloopPoll(&mainloop) >= 0)
{
otSysMainloopProcess(instance, &mainloop);
#if OPENTHREAD_USE_CONSOLE
otxConsoleProcess(&mainloop);
#endif
}
else if (errno != EINTR)
{
perror("select");
exit(OT_EXIT_FAILURE);
}
}
#if OPENTHREAD_USE_CONSOLE
otxConsoleDeinit();
#endif
otInstanceFinalize(instance);
otSysDeinit();
return 0;
}