blob: 2f5e3a898ba4605fc8c6e9975a01bb9629e4a56a [file] [log] [blame]
/*
* Copyright (c) 2016, The OpenThread Authors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/**
* @file
* This file implements the diagnostics module.
*/
#include <openthread/config.h>
#include <stdio.h>
#include <stdlib.h>
#include "utils/wrap_string.h"
#include "diag_process.hpp"
#include "common/code_utils.hpp"
namespace ot {
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;
otRadioFrame * Diag::sTxPacket;
bool Diag::sRepeatActive;
otInstance *Diag::sContext;
void Diag::Init(otInstance *aInstance)
{
sContext = aInstance;
sChannel = 20;
sTxPower = 0;
sTxPeriod = 0;
sTxLen = 0;
sTxPackets = 0;
sRepeatActive = false;
memset(&sStats, 0, sizeof(struct DiagStats));
sTxPacket = otPlatRadioGetTransmitBuffer(sContext);
otPlatDiagChannelSet(sChannel);
otPlatDiagTxPowerSet(sTxPower);
}
char *Diag::ProcessCmd(int argc, char *argv[])
{
unsigned int i;
if (argc == 0)
{
snprintf(sDiagOutput, sizeof(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, argc > 1 ? &argv[1] : NULL, sDiagOutput, sizeof(sDiagOutput));
break;
}
}
// more platform specific features will be processed under platform layer
if (i == sizeof(sCommands) / sizeof(sCommands[0]))
{
otPlatDiagProcess(sContext, argc, argv, sDiagOutput, sizeof(sDiagOutput));
}
}
return sDiagOutput;
}
bool Diag::isEnabled()
{
return otPlatDiagModeGet();
}
void Diag::AppendErrorResult(otError aError, char *aOutput, size_t aOutputMaxLen)
{
if (aError != OT_ERROR_NONE)
{
snprintf(aOutput, aOutputMaxLen, "failed\r\nstatus %#x\r\n", aError);
}
}
void Diag::ProcessStart(int argc, char *argv[], char *aOutput, size_t aOutputMaxLen)
{
otError error = OT_ERROR_NONE;
// enable radio
otPlatRadioEnable(sContext);
// enable promiscuous mode
otPlatRadioSetPromiscuous(sContext, true);
// stop timer
otPlatAlarmStop(sContext);
// start to listen on the default channel
SuccessOrExit(error = otPlatRadioReceive(sContext, sChannel));
// enable diagnostics mode
otPlatDiagModeSet(true);
memset(&sStats, 0, sizeof(struct DiagStats));
snprintf(aOutput, aOutputMaxLen, "start diagnostics mode\r\nstatus 0x%02x\r\n", error);
exit:
OT_UNUSED_VARIABLE(argc);
OT_UNUSED_VARIABLE(argv);
AppendErrorResult(error, aOutput, aOutputMaxLen);
}
void Diag::ProcessStop(int argc, char *argv[], char *aOutput, size_t aOutputMaxLen)
{
otError error = OT_ERROR_NONE;
VerifyOrExit(otPlatDiagModeGet(), error = OT_ERROR_INVALID_STATE);
otPlatAlarmStop(sContext);
otPlatDiagModeSet(false);
otPlatRadioSetPromiscuous(sContext, false);
snprintf(aOutput, aOutputMaxLen, "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<int>(sStats.received_packets), static_cast<int>(sStats.sent_packets), static_cast<int>(sStats.first_rssi),
static_cast<int>(sStats.first_lqi), error);
exit:
OT_UNUSED_VARIABLE(argc);
OT_UNUSED_VARIABLE(argv);
AppendErrorResult(error, aOutput, aOutputMaxLen);
}
otError Diag::ParseLong(char *argv, long &value)
{
char *endptr;
value = strtol(argv, &endptr, 0);
return (*endptr == '\0') ? OT_ERROR_NONE : OT_ERROR_PARSE;
}
void Diag::TxPacket()
{
sTxPacket->mLength = sTxLen;
sTxPacket->mChannel = sChannel;
sTxPacket->mPower = sTxPower;
for (uint8_t i = 0; i < sTxLen; i++)
{
sTxPacket->mPsdu[i] = i;
}
otPlatRadioTransmit(sContext, sTxPacket);
}
void Diag::ProcessChannel(int argc, char *argv[], char *aOutput, size_t aOutputMaxLen)
{
otError error = OT_ERROR_NONE;
VerifyOrExit(otPlatDiagModeGet(), error = OT_ERROR_INVALID_STATE);
if (argc == 0)
{
snprintf(aOutput, aOutputMaxLen, "channel: %d\r\n", sChannel);
}
else
{
long value;
SuccessOrExit(error = ParseLong(argv[0], value));
VerifyOrExit(value >= OT_RADIO_CHANNEL_MIN && value <= OT_RADIO_CHANNEL_MAX, error = OT_ERROR_INVALID_ARGS);
sChannel = static_cast<uint8_t>(value);
// listen on the set channel immediately
otPlatRadioReceive(sContext, sChannel);
otPlatDiagChannelSet(sChannel);
snprintf(aOutput, aOutputMaxLen, "set channel to %d\r\nstatus 0x%02x\r\n", sChannel, error);
}
exit:
AppendErrorResult(error, aOutput, aOutputMaxLen);
}
void Diag::ProcessPower(int argc, char *argv[], char *aOutput, size_t aOutputMaxLen)
{
otError error = OT_ERROR_NONE;
VerifyOrExit(otPlatDiagModeGet(), error = OT_ERROR_INVALID_STATE);
if (argc == 0)
{
snprintf(aOutput, aOutputMaxLen, "tx power: %d dBm\r\n", sTxPower);
}
else
{
long value;
SuccessOrExit(error = ParseLong(argv[0], value));
sTxPower = static_cast<int8_t>(value);
otPlatDiagTxPowerSet(sTxPower);
snprintf(aOutput, aOutputMaxLen, "set tx power to %d dBm\r\nstatus 0x%02x\r\n", sTxPower, error);
}
exit:
AppendErrorResult(error, aOutput, aOutputMaxLen);
}
void Diag::ProcessSend(int argc, char *argv[], char *aOutput, size_t aOutputMaxLen)
{
otError error = OT_ERROR_NONE;
long value;
VerifyOrExit(otPlatDiagModeGet(), error = OT_ERROR_INVALID_STATE);
VerifyOrExit(argc == 2, error = OT_ERROR_INVALID_ARGS);
SuccessOrExit(error = ParseLong(argv[0], value));
sTxPackets = static_cast<uint32_t>(value);
SuccessOrExit(error = ParseLong(argv[1], value));
VerifyOrExit(value <= OT_RADIO_FRAME_MAX_SIZE, error = OT_ERROR_INVALID_ARGS);
sTxLen = static_cast<uint8_t>(value);
snprintf(aOutput, aOutputMaxLen, "sending %#x packet(s), length %#x\r\nstatus 0x%02x\r\n", static_cast<int>(sTxPackets), static_cast<int>(sTxLen), error);
TxPacket();
exit:
AppendErrorResult(error, aOutput, aOutputMaxLen);
}
void Diag::ProcessRepeat(int argc, char *argv[], char *aOutput, size_t aOutputMaxLen)
{
otError error = OT_ERROR_NONE;
VerifyOrExit(otPlatDiagModeGet(), error = OT_ERROR_INVALID_STATE);
VerifyOrExit(argc > 0, error = OT_ERROR_INVALID_ARGS);
if (strcmp(argv[0], "stop") == 0)
{
otPlatAlarmStop(sContext);
sRepeatActive = false;
snprintf(aOutput, aOutputMaxLen, "repeated packet transmission is stopped\r\nstatus 0x%02x\r\n", error);
}
else
{
long value;
VerifyOrExit(argc == 2, error = OT_ERROR_INVALID_ARGS);
SuccessOrExit(error = ParseLong(argv[0], value));
sTxPeriod = static_cast<uint32_t>(value);
SuccessOrExit(error = ParseLong(argv[1], value));
VerifyOrExit(value <= OT_RADIO_FRAME_MAX_SIZE, error = OT_ERROR_INVALID_ARGS);
sTxLen = static_cast<uint8_t>(value);
sRepeatActive = true;
uint32_t now = otPlatAlarmGetNow();
otPlatAlarmStartAt(sContext, now, sTxPeriod);
snprintf(aOutput, aOutputMaxLen, "sending packets of length %#x at the delay of %#x ms\r\nstatus 0x%02x\r\n", static_cast<int>(sTxLen), static_cast<int>(sTxPeriod), error);
}
exit:
AppendErrorResult(error, aOutput, aOutputMaxLen);
}
void Diag::ProcessSleep(int argc, char *argv[], char *aOutput, size_t aOutputMaxLen)
{
otError error = OT_ERROR_NONE;
VerifyOrExit(otPlatDiagModeGet(), error = OT_ERROR_INVALID_STATE);
otPlatRadioSleep(sContext);
snprintf(aOutput, aOutputMaxLen, "sleeping now...\r\n");
exit:
OT_UNUSED_VARIABLE(argc);
OT_UNUSED_VARIABLE(argv);
AppendErrorResult(error, aOutput, aOutputMaxLen);
}
void Diag::ProcessStats(int argc, char *argv[], char *aOutput, size_t aOutputMaxLen)
{
otError error = OT_ERROR_NONE;
VerifyOrExit(otPlatDiagModeGet(), error = OT_ERROR_INVALID_STATE);
snprintf(aOutput, aOutputMaxLen, "received packets: %d\r\nsent packets: %d\r\nfirst received packet: rssi=%d, lqi=%d\r\n",
static_cast<int>(sStats.received_packets), static_cast<int>(sStats.sent_packets),
static_cast<int>(sStats.first_rssi), static_cast<int>(sStats.first_lqi));
exit:
OT_UNUSED_VARIABLE(argc);
OT_UNUSED_VARIABLE(argv);
AppendErrorResult(error, aOutput, aOutputMaxLen);
}
void Diag::DiagTransmitDone(otInstance *aInstance, otError aError)
{
OT_UNUSED_VARIABLE(aInstance);
if (aError == OT_ERROR_NONE)
{
sStats.sent_packets++;
if (sTxPackets > 1)
{
sTxPackets--;
TxPacket();
}
}
else
{
TxPacket();
}
}
void Diag::DiagReceiveDone(otInstance *aInstance, otRadioFrame *aFrame, otError aError)
{
OT_UNUSED_VARIABLE(aInstance);
if (aError == OT_ERROR_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++;
}
otPlatDiagRadioReceived(aInstance, aFrame, aError);
otPlatRadioReceive(aInstance, sChannel);
}
void Diag::AlarmFired(otInstance *aInstance)
{
if(sRepeatActive)
{
uint32_t now = otPlatAlarmGetNow();
TxPacket();
otPlatAlarmStartAt(aInstance, now, sTxPeriod);
}
else
{
otPlatDiagAlarmCallback(aInstance);
}
}
extern "C" void otPlatDiagAlarmFired(otInstance *aInstance)
{
Diag::AlarmFired(aInstance);
}
extern "C" void otPlatDiagRadioTransmitDone(otInstance *aInstance, otRadioFrame *aFrame, otError aError)
{
OT_UNUSED_VARIABLE(aFrame);
Diag::DiagTransmitDone(aInstance, aError);
}
extern "C" void otPlatDiagRadioReceiveDone(otInstance *aInstance, otRadioFrame *aFrame, otError aError)
{
Diag::DiagReceiveDone(aInstance, aFrame, aError);
}
} // namespace Diagnostics
} // namespace ot