From f0379f6d6e1dc79d169d5daaec4c93be59010b6e Mon Sep 17 00:00:00 2001 From: Zhanglong Xia Date: Wed, 5 Jun 2024 11:20:28 +0800 Subject: [PATCH] [diag] add diag output callback The length of diag output messages is limited by the diag buffer size. Developers have to change the diag buffer size to allow diag module to output long messages. If diag output messages become longer and longer, developers have to keep changing the diag buffer size. This commit adds an output callback to diag module to output diag messages. Then the length of diag output messages won't be limited by the diag buffer size. --- examples/platforms/simulation/diag.c | 7 + include/openthread/diag.h | 32 ++-- include/openthread/instance.h | 2 +- include/openthread/platform/diag.h | 31 ++-- src/cli/cli.cpp | 19 +- src/cli/cli.hpp | 5 + src/core/api/diags_api.cpp | 12 +- src/core/diags/factory_diags.cpp | 252 +++++++++++++-------------- src/core/diags/factory_diags.hpp | 50 ++++-- src/lib/spinel/radio_spinel.cpp | 49 ++++-- src/lib/spinel/radio_spinel.hpp | 24 ++- src/ncp/ncp_base.cpp | 60 ++++++- src/ncp/ncp_base.hpp | 10 ++ src/posix/platform/radio.cpp | 100 ++++++++--- src/posix/platform/rcp_caps_diag.cpp | 18 +- src/posix/platform/rcp_caps_diag.hpp | 25 ++- tests/fuzz/fuzzer_platform.cpp | 15 +- tests/unit/test_platform.cpp | 28 ++- tests/unit/test_platform.h | 1 + 19 files changed, 488 insertions(+), 252 deletions(-) diff --git a/examples/platforms/simulation/diag.c b/examples/platforms/simulation/diag.c index dcd9f1d9f7ad..8ce6efcfdefd 100644 --- a/examples/platforms/simulation/diag.c +++ b/examples/platforms/simulation/diag.c @@ -58,6 +58,13 @@ static bool sGpioValue = false; static uint8_t sRawPowerSetting[OPENTHREAD_CONFIG_POWER_CALIBRATION_RAW_POWER_SETTING_SIZE]; static uint16_t sRawPowerSettingLength = 0; +void otPlatDiagSetOutputCallback(otInstance *aInstance, otPlatDiagOutputCallback aCallback, void *aContext) +{ + OT_UNUSED_VARIABLE(aInstance); + OT_UNUSED_VARIABLE(aCallback); + OT_UNUSED_VARIABLE(aContext); +} + void otPlatDiagModeSet(bool aMode) { sDiagMode = aMode; } bool otPlatDiagModeGet(void) { return sDiagMode; } diff --git a/include/openthread/diag.h b/include/openthread/diag.h index 217abf6cb380..710fbeb21305 100644 --- a/include/openthread/diag.h +++ b/include/openthread/diag.h @@ -36,6 +36,7 @@ #define OPENTHREAD_DIAG_H_ #include +#include #ifdef __cplusplus extern "C" { @@ -51,39 +52,38 @@ extern "C" { * */ +/* Represents the pointer to callback to output diag messages.*/ +typedef otPlatDiagOutputCallback otDiagOutputCallback; + /** - * Processes a factory diagnostics command line. + * Sets the diag output callback. * - * The output of this function (the content written to @p aOutput) MUST terminate with `\0` and the `\0` is within the - * output buffer. + * @param[in] aInstance The OpenThread instance structure. + * @param[in] aCallback A pointer to a function that is called on outputting diag messages. + * @param[in] aContext A pointer to the user context. + * + */ +void otDiagSetOutputCallback(otInstance *aInstance, otDiagOutputCallback aCallback, void *aContext); + +/** + * Processes a factory diagnostics command line. * * @param[in] aInstance A pointer to an OpenThread instance. * @param[in] aArgsLength The number of elements in @p aArgs. * @param[in] aArgs An array of arguments. - * @param[out] aOutput The diagnostics execution result. - * @param[in] aOutputMaxLen The output buffer size. * * @retval OT_ERROR_INVALID_ARGS The command is supported but invalid arguments provided. * @retval OT_ERROR_NONE The command is successfully process. * @retval OT_ERROR_NOT_IMPLEMENTED The command is not supported. * */ -otError otDiagProcessCmd(otInstance *aInstance, - uint8_t aArgsLength, - char *aArgs[], - char *aOutput, - size_t aOutputMaxLen); +otError otDiagProcessCmd(otInstance *aInstance, uint8_t aArgsLength, char *aArgs[]); /** * Processes a factory diagnostics command line. * - * The output of this function (the content written to @p aOutput) MUST terminate with `\0` and the `\0` is within the - * output buffer. - * * @param[in] aInstance A pointer to an OpenThread instance. * @param[in] aString A NULL-terminated input string. - * @param[out] aOutput The diagnostics execution result. - * @param[in] aOutputMaxLen The output buffer size. * * @retval OT_ERROR_NONE The command is successfully process. * @retval OT_ERROR_INVALID_ARGS The command is supported but invalid arguments provided. @@ -91,7 +91,7 @@ otError otDiagProcessCmd(otInstance *aInstance, * @retval OT_ERROR_NO_BUFS The command string is too long. * */ -otError otDiagProcessCmdLine(otInstance *aInstance, const char *aString, char *aOutput, size_t aOutputMaxLen); +otError otDiagProcessCmdLine(otInstance *aInstance, const char *aString); /** * Indicates whether or not the factory diagnostics mode is enabled. diff --git a/include/openthread/instance.h b/include/openthread/instance.h index 5c69dfe8aa39..6c88dfab27d9 100644 --- a/include/openthread/instance.h +++ b/include/openthread/instance.h @@ -53,7 +53,7 @@ extern "C" { * @note This number versions both OpenThread platform and user APIs. * */ -#define OPENTHREAD_API_VERSION (420) +#define OPENTHREAD_API_VERSION (421) /** * @addtogroup api-instance diff --git a/include/openthread/platform/diag.h b/include/openthread/platform/diag.h index 7bc229946485..888ef448c142 100644 --- a/include/openthread/platform/diag.h +++ b/include/openthread/platform/diag.h @@ -67,27 +67,38 @@ typedef enum } otGpioMode; /** - * Processes a factory diagnostics command line. + * Pointer to callback to output platform diag messages. + * + * @param[in] aFormat The format string. + * @param[in] aArguments The format string arguments. + * @param[out] aContext A pointer to the user context. + * + */ +typedef void (*otPlatDiagOutputCallback)(const char *aFormat, va_list aArguments, void *aContext); + +/** + * Sets the platform diag output callback. * - * The output of this function (the content written to @p aOutput) MUST terminate with `\0` and the `\0` is within the - * output buffer. + * @param[in] aInstance The OpenThread instance structure. + * @param[in] aCallback A pointer to a function that is called on outputting diag messages. + * @param[in] aContext A pointer to the user context. + * + */ +void otPlatDiagSetOutputCallback(otInstance *aInstance, otPlatDiagOutputCallback aCallback, void *aContext); + +/** + * Processes a factory diagnostics command line. * * @param[in] aInstance The OpenThread instance for current request. * @param[in] aArgsLength The number of arguments in @p aArgs. * @param[in] aArgs The arguments of diagnostics command line. - * @param[out] aOutput The diagnostics execution result. - * @param[in] aOutputMaxLen The output buffer size. * * @retval OT_ERROR_INVALID_ARGS The command is supported but invalid arguments provided. * @retval OT_ERROR_NONE The command is successfully process. * @retval OT_ERROR_INVALID_COMMAND The command is not valid or not supported. * */ -otError otPlatDiagProcess(otInstance *aInstance, - uint8_t aArgsLength, - char *aArgs[], - char *aOutput, - size_t aOutputMaxLen); +otError otPlatDiagProcess(otInstance *aInstance, uint8_t aArgsLength, char *aArgs[]); /** * Enables/disables the factory diagnostics mode. diff --git a/src/cli/cli.cpp b/src/cli/cli.cpp index d70978872bfc..2b6cd23c521b 100644 --- a/src/cli/cli.cpp +++ b/src/cli/cli.cpp @@ -169,6 +169,10 @@ Interpreter::Interpreter(Instance *aInstance, otCliOutputCallback aCallback, voi #if (OPENTHREAD_FTD || OPENTHREAD_MTD) && OPENTHREAD_CONFIG_CLI_REGISTER_IP6_RECV_CALLBACK otIp6SetReceiveCallback(GetInstancePtr(), &Interpreter::HandleIp6Receive, this); #endif +#if OPENTHREAD_CONFIG_DIAG_ENABLE + otDiagSetOutputCallback(GetInstancePtr(), &Interpreter::HandleDiagOutput, this); +#endif + ClearAllBytes(mUserCommands); OutputPrompt(); @@ -210,19 +214,20 @@ void Interpreter::OutputResult(otError aError) #if OPENTHREAD_CONFIG_DIAG_ENABLE template <> otError Interpreter::Process(Arg aArgs[]) { - otError error; - char *args[kMaxArgs]; - char output[OPENTHREAD_CONFIG_DIAG_OUTPUT_BUFFER_SIZE]; + char *args[kMaxArgs]; // all diagnostics related features are processed within diagnostics module Arg::CopyArgsToStringArray(aArgs, args); - error = otDiagProcessCmd(GetInstancePtr(), Arg::GetArgsLength(aArgs), args, output, sizeof(output)); - - OutputFormat("%s", output); + return otDiagProcessCmd(GetInstancePtr(), Arg::GetArgsLength(aArgs), args); +} - return error; +void Interpreter::HandleDiagOutput(const char *aFormat, va_list aArguments, void *aContext) +{ + static_cast(aContext)->HandleDiagOutput(aFormat, aArguments); } + +void Interpreter::HandleDiagOutput(const char *aFormat, va_list aArguments) { OutputFormatV(aFormat, aArguments); } #endif template <> otError Interpreter::Process(Arg aArgs[]) diff --git a/src/cli/cli.hpp b/src/cli/cli.hpp index d5fe30ad9773..65af2e839b79 100644 --- a/src/cli/cli.hpp +++ b/src/cli/cli.hpp @@ -339,6 +339,11 @@ class Interpreter : public OutputImplementer, public Utils #endif // OPENTHREAD_FTD || OPENTHREAD_MTD +#if OPENTHREAD_CONFIG_DIAG_ENABLE + static void HandleDiagOutput(const char *aFormat, va_list aArguments, void *aContext); + void HandleDiagOutput(const char *aFormat, va_list aArguments); +#endif + void SetCommandTimeout(uint32_t aTimeoutMilli); static void HandleTimer(Timer &aTimer); diff --git a/src/core/api/diags_api.cpp b/src/core/api/diags_api.cpp index a87bde581358..cb8488ddc461 100644 --- a/src/core/api/diags_api.cpp +++ b/src/core/api/diags_api.cpp @@ -42,18 +42,22 @@ using namespace ot; -otError otDiagProcessCmdLine(otInstance *aInstance, const char *aString, char *aOutput, size_t aOutputMaxLen) +otError otDiagProcessCmdLine(otInstance *aInstance, const char *aString) { AssertPointerIsNotNull(aString); - return AsCoreType(aInstance).Get().ProcessLine(aString, aOutput, aOutputMaxLen); + return AsCoreType(aInstance).Get().ProcessLine(aString); } -otError otDiagProcessCmd(otInstance *aInstance, uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen) +otError otDiagProcessCmd(otInstance *aInstance, uint8_t aArgsLength, char *aArgs[]) { - return AsCoreType(aInstance).Get().ProcessCmd(aArgsLength, aArgs, aOutput, aOutputMaxLen); + return AsCoreType(aInstance).Get().ProcessCmd(aArgsLength, aArgs); } bool otDiagIsEnabled(otInstance *aInstance) { return AsCoreType(aInstance).Get().IsEnabled(); } +void otDiagSetOutputCallback(otInstance *aInstance, otDiagOutputCallback aCallback, void *aContext) +{ + AsCoreType(aInstance).Get().SetOutputCallback(aCallback, aContext); +} #endif // OPENTHREAD_CONFIG_DIAG_ENABLE diff --git a/src/core/diags/factory_diags.cpp b/src/core/diags/factory_diags.cpp index 78845f1aab67..699d9803d23b 100644 --- a/src/core/diags/factory_diags.cpp +++ b/src/core/diags/factory_diags.cpp @@ -49,17 +49,11 @@ #include "utils/parse_cmdline.hpp" OT_TOOL_WEAK -otError otPlatDiagProcess(otInstance *aInstance, - uint8_t aArgsLength, - char *aArgs[], - char *aOutput, - size_t aOutputMaxLen) +otError otPlatDiagProcess(otInstance *aInstance, uint8_t aArgsLength, char *aArgs[]) { OT_UNUSED_VARIABLE(aArgsLength); OT_UNUSED_VARIABLE(aArgs); OT_UNUSED_VARIABLE(aInstance); - OT_UNUSED_VARIABLE(aOutput); - OT_UNUSED_VARIABLE(aOutputMaxLen); return ot::kErrorInvalidCommand; } @@ -84,10 +78,12 @@ const struct Diags::Command Diags::sCommands[] = { Diags::Diags(Instance &aInstance) : InstanceLocator(aInstance) + , mOutputCallback(nullptr) + , mOutputContext(nullptr) { } -Error Diags::ProcessChannel(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen) +Error Diags::ProcessChannel(uint8_t aArgsLength, char *aArgs[]) { Error error = kErrorNone; long value; @@ -100,11 +96,11 @@ Error Diags::ProcessChannel(uint8_t aArgsLength, char *aArgs[], char *aOutput, s otPlatDiagChannelSet(static_cast(value)); exit: - AppendErrorResult(error, aOutput, aOutputMaxLen); + AppendErrorResult(error); return error; } -Error Diags::ProcessPower(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen) +Error Diags::ProcessPower(uint8_t aArgsLength, char *aArgs[]) { Error error = kErrorNone; long value; @@ -116,35 +112,35 @@ Error Diags::ProcessPower(uint8_t aArgsLength, char *aArgs[], char *aOutput, siz otPlatDiagTxPowerSet(static_cast(value)); exit: - AppendErrorResult(error, aOutput, aOutputMaxLen); + AppendErrorResult(error); return error; } -Error Diags::ProcessEcho(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen) +Error Diags::ProcessEcho(uint8_t aArgsLength, char *aArgs[]) { Error error = kErrorNone; if (aArgsLength == 1) { - snprintf(aOutput, aOutputMaxLen, "%s\r\n", aArgs[0]); + Output("%s\r\n", aArgs[0]); } else if ((aArgsLength == 2) && (strcmp(aArgs[0], "-n") == 0)) { - const uint8_t kReservedLen = 3; // 1 byte '\r', 1 byte '\n' and 1 byte '\0' - uint32_t outputMaxLen = static_cast(aOutputMaxLen) - kReservedLen; - long value; - uint32_t i; - uint32_t number; + static constexpr uint16_t kOutputLen = OPENTHREAD_CONFIG_DIAG_OUTPUT_BUFFER_SIZE; + char output[kOutputLen]; + long value; + uint32_t i; + uint32_t number; SuccessOrExit(error = ParseLong(aArgs[1], value)); - number = Min(static_cast(value), outputMaxLen); + number = Min(static_cast(value), static_cast(kOutputLen)); for (i = 0; i < number; i++) { - aOutput[i] = '0' + i % 10; + output[i] = '0' + i % 10; } - snprintf(&aOutput[i], aOutputMaxLen - i, "\r\n"); + Output("%s\r\n", output); } else { @@ -152,28 +148,24 @@ Error Diags::ProcessEcho(uint8_t aArgsLength, char *aArgs[], char *aOutput, size } exit: - AppendErrorResult(error, aOutput, aOutputMaxLen); + AppendErrorResult(error); return error; } -Error Diags::ProcessStart(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen) +Error Diags::ProcessStart(uint8_t aArgsLength, char *aArgs[]) { OT_UNUSED_VARIABLE(aArgsLength); OT_UNUSED_VARIABLE(aArgs); - OT_UNUSED_VARIABLE(aOutput); - OT_UNUSED_VARIABLE(aOutputMaxLen); otPlatDiagModeSet(true); return kErrorNone; } -Error Diags::ProcessStop(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen) +Error Diags::ProcessStop(uint8_t aArgsLength, char *aArgs[]) { OT_UNUSED_VARIABLE(aArgsLength); OT_UNUSED_VARIABLE(aArgs); - OT_UNUSED_VARIABLE(aOutput); - OT_UNUSED_VARIABLE(aOutputMaxLen); otPlatDiagModeSet(false); @@ -210,11 +202,13 @@ Diags::Diags(Instance &aInstance) , mTxLen(0) , mRepeatActive(false) , mDiagSendOn(false) + , mOutputCallback(nullptr) + , mOutputContext(nullptr) { mStats.Clear(); } -Error Diags::ProcessChannel(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen) +Error Diags::ProcessChannel(uint8_t aArgsLength, char *aArgs[]) { Error error = kErrorNone; @@ -222,7 +216,7 @@ Error Diags::ProcessChannel(uint8_t aArgsLength, char *aArgs[], char *aOutput, s if (aArgsLength == 0) { - snprintf(aOutput, aOutputMaxLen, "channel: %d\r\n", mChannel); + Output("channel: %d\r\n", mChannel); } else { @@ -235,15 +229,15 @@ Error Diags::ProcessChannel(uint8_t aArgsLength, char *aArgs[], char *aOutput, s IgnoreError(Get().Receive(mChannel)); otPlatDiagChannelSet(mChannel); - snprintf(aOutput, aOutputMaxLen, "set channel to %d\r\nstatus 0x%02x\r\n", mChannel, error); + Output("set channel to %d\r\nstatus 0x%02x\r\n", mChannel, error); } exit: - AppendErrorResult(error, aOutput, aOutputMaxLen); + AppendErrorResult(error); return error; } -Error Diags::ProcessPower(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen) +Error Diags::ProcessPower(uint8_t aArgsLength, char *aArgs[]) { Error error = kErrorNone; @@ -251,7 +245,7 @@ Error Diags::ProcessPower(uint8_t aArgsLength, char *aArgs[], char *aOutput, siz if (aArgsLength == 0) { - snprintf(aOutput, aOutputMaxLen, "tx power: %d dBm\r\n", mTxPower); + Output("tx power: %d dBm\r\n", mTxPower); } else { @@ -263,15 +257,15 @@ Error Diags::ProcessPower(uint8_t aArgsLength, char *aArgs[], char *aOutput, siz SuccessOrExit(error = Get().SetTransmitPower(mTxPower)); otPlatDiagTxPowerSet(mTxPower); - snprintf(aOutput, aOutputMaxLen, "set tx power to %d dBm\r\nstatus 0x%02x\r\n", mTxPower, error); + Output("set tx power to %d dBm\r\nstatus 0x%02x\r\n", mTxPower, error); } exit: - AppendErrorResult(error, aOutput, aOutputMaxLen); + AppendErrorResult(error); return error; } -Error Diags::ProcessRepeat(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen) +Error Diags::ProcessRepeat(uint8_t aArgsLength, char *aArgs[]) { Error error = kErrorNone; @@ -282,7 +276,7 @@ Error Diags::ProcessRepeat(uint8_t aArgsLength, char *aArgs[], char *aOutput, si { otPlatAlarmMilliStop(&GetInstance()); mRepeatActive = false; - snprintf(aOutput, aOutputMaxLen, "repeated packet transmission is stopped\r\nstatus 0x%02x\r\n", error); + Output("repeated packet transmission is stopped\r\nstatus 0x%02x\r\n", error); } else { @@ -301,16 +295,16 @@ Error Diags::ProcessRepeat(uint8_t aArgsLength, char *aArgs[], char *aOutput, si mRepeatActive = true; uint32_t now = otPlatAlarmMilliGetNow(); otPlatAlarmMilliStartAt(&GetInstance(), now, mTxPeriod); - snprintf(aOutput, aOutputMaxLen, "sending packets of length %#x at the delay of %#x ms\r\nstatus 0x%02x\r\n", - static_cast(mTxLen), static_cast(mTxPeriod), error); + Output("sending packets of length %#x at the delay of %#x ms\r\nstatus 0x%02x\r\n", static_cast(mTxLen), + static_cast(mTxPeriod), error); } exit: - AppendErrorResult(error, aOutput, aOutputMaxLen); + AppendErrorResult(error); return error; } -Error Diags::ProcessSend(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen) +Error Diags::ProcessSend(uint8_t aArgsLength, char *aArgs[]) { Error error = kErrorNone; long value; @@ -326,16 +320,16 @@ Error Diags::ProcessSend(uint8_t aArgsLength, char *aArgs[], char *aOutput, size VerifyOrExit(value >= OT_RADIO_FRAME_MIN_SIZE, error = kErrorInvalidArgs); mTxLen = static_cast(value); - snprintf(aOutput, aOutputMaxLen, "sending %#x packet(s), length %#x\r\nstatus 0x%02x\r\n", - static_cast(mTxPackets), static_cast(mTxLen), error); + Output("sending %#x packet(s), length %#x\r\nstatus 0x%02x\r\n", static_cast(mTxPackets), + static_cast(mTxLen), error); TransmitPacket(); exit: - AppendErrorResult(error, aOutput, aOutputMaxLen); + AppendErrorResult(error); return error; } -Error Diags::ProcessStart(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen) +Error Diags::ProcessStart(uint8_t aArgsLength, char *aArgs[]) { OT_UNUSED_VARIABLE(aArgsLength); OT_UNUSED_VARIABLE(aArgs); @@ -356,14 +350,14 @@ Error Diags::ProcessStart(uint8_t aArgsLength, char *aArgs[], char *aOutput, siz SuccessOrExit(error = Get().SetTransmitPower(mTxPower)); otPlatDiagModeSet(true); mStats.Clear(); - snprintf(aOutput, aOutputMaxLen, "start diagnostics mode\r\nstatus 0x%02x\r\n", error); + Output("start diagnostics mode\r\nstatus 0x%02x\r\n", error); exit: - AppendErrorResult(error, aOutput, aOutputMaxLen); + AppendErrorResult(error); return error; } -Error Diags::ProcessStats(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen) +Error Diags::ProcessStats(uint8_t aArgsLength, char *aArgs[]) { Error error = kErrorNone; @@ -372,26 +366,25 @@ Error Diags::ProcessStats(uint8_t aArgsLength, char *aArgs[], char *aOutput, siz if ((aArgsLength == 1) && (strcmp(aArgs[0], "clear") == 0)) { mStats.Clear(); - snprintf(aOutput, aOutputMaxLen, "stats cleared\r\n"); + Output("stats cleared\r\n"); } else { VerifyOrExit(aArgsLength == 0, error = kErrorInvalidArgs); - snprintf(aOutput, aOutputMaxLen, - "received packets: %d\r\nsent packets: %d\r\n" - "first received packet: rssi=%d, lqi=%d\r\n" - "last received packet: rssi=%d, lqi=%d\r\n", - static_cast(mStats.mReceivedPackets), static_cast(mStats.mSentPackets), - static_cast(mStats.mFirstRssi), static_cast(mStats.mFirstLqi), - static_cast(mStats.mLastRssi), static_cast(mStats.mLastLqi)); + Output("received packets: %d\r\nsent packets: %d\r\n" + "first received packet: rssi=%d, lqi=%d\r\n" + "last received packet: rssi=%d, lqi=%d\r\n", + static_cast(mStats.mReceivedPackets), static_cast(mStats.mSentPackets), + static_cast(mStats.mFirstRssi), static_cast(mStats.mFirstLqi), + static_cast(mStats.mLastRssi), static_cast(mStats.mLastLqi)); } exit: - AppendErrorResult(error, aOutput, aOutputMaxLen); + AppendErrorResult(error); return error; } -Error Diags::ProcessStop(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen) +Error Diags::ProcessStop(uint8_t aArgsLength, char *aArgs[]) { OT_UNUSED_VARIABLE(aArgsLength); OT_UNUSED_VARIABLE(aArgs); @@ -404,17 +397,16 @@ Error Diags::ProcessStop(uint8_t aArgsLength, char *aArgs[], char *aOutput, size otPlatDiagModeSet(false); Get().SetPromiscuous(false); - snprintf(aOutput, aOutputMaxLen, - "received packets: %d\r\nsent packets: %d\r\n" - "first received packet: rssi=%d, lqi=%d\r\n" - "last received packet: rssi=%d, lqi=%d\r\n" - "\nstop diagnostics mode\r\nstatus 0x%02x\r\n", - static_cast(mStats.mReceivedPackets), static_cast(mStats.mSentPackets), - static_cast(mStats.mFirstRssi), static_cast(mStats.mFirstLqi), - static_cast(mStats.mLastRssi), static_cast(mStats.mLastLqi), error); + Output("received packets: %d\r\nsent packets: %d\r\n" + "first received packet: rssi=%d, lqi=%d\r\n" + "last received packet: rssi=%d, lqi=%d\r\n" + "\nstop diagnostics mode\r\nstatus 0x%02x\r\n", + static_cast(mStats.mReceivedPackets), static_cast(mStats.mSentPackets), + static_cast(mStats.mFirstRssi), static_cast(mStats.mFirstLqi), static_cast(mStats.mLastRssi), + static_cast(mStats.mLastLqi), error); exit: - AppendErrorResult(error, aOutput, aOutputMaxLen); + AppendErrorResult(error); return error; } @@ -432,7 +424,7 @@ void Diags::TransmitPacket(void) IgnoreError(Get().Transmit(*static_cast(mTxPacket))); } -Error Diags::ProcessRadio(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen) +Error Diags::ProcessRadio(uint8_t aArgsLength, char *aArgs[]) { Error error = kErrorInvalidArgs; @@ -442,7 +434,7 @@ Error Diags::ProcessRadio(uint8_t aArgsLength, char *aArgs[], char *aOutput, siz if (strcmp(aArgs[0], "sleep") == 0) { SuccessOrExit(error = Get().Sleep()); - snprintf(aOutput, aOutputMaxLen, "set radio from receive to sleep \r\nstatus 0x%02x\r\n", error); + Output("set radio from receive to sleep \r\nstatus 0x%02x\r\n", error); } else if (strcmp(aArgs[0], "receive") == 0) { @@ -451,8 +443,7 @@ Error Diags::ProcessRadio(uint8_t aArgsLength, char *aArgs[], char *aOutput, siz otPlatDiagChannelSet(mChannel); otPlatDiagTxPowerSet(mTxPower); - snprintf(aOutput, aOutputMaxLen, "set radio from sleep to receive on channel %d\r\nstatus 0x%02x\r\n", mChannel, - error); + Output("set radio from sleep to receive on channel %d\r\nstatus 0x%02x\r\n", mChannel, error); } else if (strcmp(aArgs[0], "state") == 0) { @@ -463,29 +454,29 @@ Error Diags::ProcessRadio(uint8_t aArgsLength, char *aArgs[], char *aOutput, siz switch (state) { case OT_RADIO_STATE_DISABLED: - snprintf(aOutput, aOutputMaxLen, "disabled\r\n"); + Output("disabled\r\n"); break; case OT_RADIO_STATE_SLEEP: - snprintf(aOutput, aOutputMaxLen, "sleep\r\n"); + Output("sleep\r\n"); break; case OT_RADIO_STATE_RECEIVE: - snprintf(aOutput, aOutputMaxLen, "receive\r\n"); + Output("receive\r\n"); break; case OT_RADIO_STATE_TRANSMIT: - snprintf(aOutput, aOutputMaxLen, "transmit\r\n"); + Output("transmit\r\n"); break; default: - snprintf(aOutput, aOutputMaxLen, "invalid\r\n"); + Output("invalid\r\n"); break; } } exit: - AppendErrorResult(error, aOutput, aOutputMaxLen); + AppendErrorResult(error); return error; } @@ -554,7 +545,7 @@ void Diags::TransmitDone(Error aError) #endif // OPENTHREAD_RADIO -Error Diags::ProcessContinuousWave(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen) +Error Diags::ProcessContinuousWave(uint8_t aArgsLength, char *aArgs[]) { Error error = kErrorInvalidArgs; @@ -571,11 +562,11 @@ Error Diags::ProcessContinuousWave(uint8_t aArgsLength, char *aArgs[], char *aOu } exit: - AppendErrorResult(error, aOutput, aOutputMaxLen); + AppendErrorResult(error); return error; } -Error Diags::ProcessStream(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen) +Error Diags::ProcessStream(uint8_t aArgsLength, char *aArgs[]) { Error error = kErrorInvalidArgs; @@ -592,7 +583,7 @@ Error Diags::ProcessStream(uint8_t aArgsLength, char *aArgs[], char *aOutput, si } exit: - AppendErrorResult(error, aOutput, aOutputMaxLen); + AppendErrorResult(error); return error; } @@ -604,7 +595,7 @@ Error Diags::GetPowerSettings(uint8_t aChannel, PowerSettings &aPowerSettings) &aPowerSettings.mRawPowerSetting.mLength); } -Error Diags::ProcessPowerSettings(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen) +Error Diags::ProcessPowerSettings(uint8_t aArgsLength, char *aArgs[]) { Error error = kErrorInvalidArgs; uint8_t channel; @@ -617,14 +608,9 @@ Error Diags::ProcessPowerSettings(uint8_t aArgsLength, char *aArgs[], char *aOut bool isPrePowerSettingsValid = false; uint8_t preChannel = 0; PowerSettings prePowerSettings; - int n; - n = snprintf(aOutput, aOutputMaxLen, - "| StartCh | EndCh | TargetPower | ActualPower | RawPowerSetting |\r\n" - "+---------+-------+-------------+-------------+-----------------+\r\n"); - VerifyOrExit((n > 0) && (n < static_cast(aOutputMaxLen)), error = kErrorNoBufs); - aOutput += n; - aOutputMaxLen -= static_cast(n); + Output("| StartCh | EndCh | TargetPower | ActualPower | RawPowerSetting |\r\n" + "+---------+-------+-------------+-------------+-----------------+\r\n"); for (channel = Radio::kChannelMin; channel <= Radio::kChannelMax + 1; channel++) { @@ -632,12 +618,8 @@ Error Diags::ProcessPowerSettings(uint8_t aArgsLength, char *aArgs[], char *aOut if (isPrePowerSettingsValid && ((powerSettings != prePowerSettings) || (error != kErrorNone))) { - n = snprintf(aOutput, aOutputMaxLen, "| %7u | %5u | %11d | %11d | %15s |\r\n", preChannel, channel - 1, - prePowerSettings.mTargetPower, prePowerSettings.mActualPower, - prePowerSettings.mRawPowerSetting.ToString().AsCString()); - VerifyOrExit((n > 0) && (n < static_cast(aOutputMaxLen)), error = kErrorNoBufs); - aOutput += n; - aOutputMaxLen -= static_cast(n); + Output("| %7u | %5u | %11d | %11d | %15s |\r\n", preChannel, channel - 1, prePowerSettings.mTargetPower, + prePowerSettings.mActualPower, prePowerSettings.mRawPowerSetting.ToString().AsCString()); isPrePowerSettingsValid = false; } @@ -657,14 +639,13 @@ Error Diags::ProcessPowerSettings(uint8_t aArgsLength, char *aArgs[], char *aOut VerifyOrExit(channel >= Radio::kChannelMin && channel <= Radio::kChannelMax, error = kErrorInvalidArgs); SuccessOrExit(error = GetPowerSettings(channel, powerSettings)); - snprintf(aOutput, aOutputMaxLen, - "TargetPower(0.01dBm): %d\r\nActualPower(0.01dBm): %d\r\nRawPowerSetting: %s\r\n", - powerSettings.mTargetPower, powerSettings.mActualPower, - powerSettings.mRawPowerSetting.ToString().AsCString()); + Output("TargetPower(0.01dBm): %d\r\nActualPower(0.01dBm): %d\r\nRawPowerSetting: %s\r\n", + powerSettings.mTargetPower, powerSettings.mActualPower, + powerSettings.mRawPowerSetting.ToString().AsCString()); } exit: - AppendErrorResult(error, aOutput, aOutputMaxLen); + AppendErrorResult(error); return error; } @@ -674,7 +655,7 @@ Error Diags::GetRawPowerSetting(RawPowerSetting &aRawPowerSetting) return otPlatDiagRadioGetRawPowerSetting(&GetInstance(), aRawPowerSetting.mData, &aRawPowerSetting.mLength); } -Error Diags::ProcessRawPowerSetting(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen) +Error Diags::ProcessRawPowerSetting(uint8_t aArgsLength, char *aArgs[]) { Error error = kErrorInvalidArgs; RawPowerSetting setting; @@ -684,7 +665,7 @@ Error Diags::ProcessRawPowerSetting(uint8_t aArgsLength, char *aArgs[], char *aO if (aArgsLength == 0) { SuccessOrExit(error = GetRawPowerSetting(setting)); - snprintf(aOutput, aOutputMaxLen, "%s\r\n", setting.ToString().AsCString()); + Output("%s\r\n", setting.ToString().AsCString()); } else if (strcmp(aArgs[0], "enable") == 0) { @@ -702,11 +683,11 @@ Error Diags::ProcessRawPowerSetting(uint8_t aArgsLength, char *aArgs[], char *aO } exit: - AppendErrorResult(error, aOutput, aOutputMaxLen); + AppendErrorResult(error); return error; } -Error Diags::ProcessGpio(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen) +Error Diags::ProcessGpio(uint8_t aArgsLength, char *aArgs[]) { Error error = kErrorInvalidArgs; long value; @@ -719,7 +700,7 @@ Error Diags::ProcessGpio(uint8_t aArgsLength, char *aArgs[], char *aOutput, size SuccessOrExit(error = ParseLong(aArgs[1], value)); gpio = static_cast(value); SuccessOrExit(error = otPlatDiagGpioGet(gpio, &level)); - snprintf(aOutput, aOutputMaxLen, "%d\r\n", level); + Output("%d\r\n", level); } else if ((aArgsLength == 3) && (strcmp(aArgs[0], "set") == 0)) { @@ -738,11 +719,11 @@ Error Diags::ProcessGpio(uint8_t aArgsLength, char *aArgs[], char *aOutput, size SuccessOrExit(error = otPlatDiagGpioGetMode(gpio, &mode)); if (mode == OT_GPIO_MODE_INPUT) { - snprintf(aOutput, aOutputMaxLen, "in\r\n"); + Output("in\r\n"); } else if (mode == OT_GPIO_MODE_OUTPUT) { - snprintf(aOutput, aOutputMaxLen, "out\r\n"); + Output("out\r\n"); } } else if ((aArgsLength == 3) && (strcmp(aArgs[2], "in") == 0)) @@ -756,15 +737,15 @@ Error Diags::ProcessGpio(uint8_t aArgsLength, char *aArgs[], char *aOutput, size } exit: - AppendErrorResult(error, aOutput, aOutputMaxLen); + AppendErrorResult(error); return error; } -void Diags::AppendErrorResult(Error aError, char *aOutput, size_t aOutputMaxLen) +void Diags::AppendErrorResult(Error aError) { if (aError != kErrorNone) { - snprintf(aOutput, aOutputMaxLen, "failed\r\nstatus %#x\r\n", aError); + Output("failed\r\nstatus %#x\r\n", aError); } } @@ -801,7 +782,7 @@ Error Diags::ParseCmd(char *aString, uint8_t &aArgsLength, char *aArgs[]) return error; } -Error Diags::ProcessLine(const char *aString, char *aOutput, size_t aOutputMaxLen) +Error Diags::ProcessLine(const char *aString) { constexpr uint16_t kMaxCommandBuffer = OPENTHREAD_CONFIG_DIAG_CMD_LINE_BUFFER_SIZE; @@ -820,27 +801,26 @@ Error Diags::ProcessLine(const char *aString, char *aOutput, size_t aOutputMaxLe switch (error) { case kErrorNone: - aOutput[0] = '\0'; // In case there is no output. - error = ProcessCmd(argCount, &args[0], aOutput, aOutputMaxLen); + error = ProcessCmd(argCount, &args[0]); break; case kErrorNoBufs: - snprintf(aOutput, aOutputMaxLen, "failed: command string too long\r\n"); + Output("failed: command string too long\r\n"); break; case kErrorInvalidArgs: - snprintf(aOutput, aOutputMaxLen, "failed: command string contains too many arguments\r\n"); + Output("failed: command string contains too many arguments\r\n"); break; default: - snprintf(aOutput, aOutputMaxLen, "failed to parse command string\r\n"); + Output("failed to parse command string\r\n"); break; } return error; } -Error Diags::ProcessCmd(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen) +Error Diags::ProcessCmd(uint8_t aArgsLength, char *aArgs[]) { Error error = kErrorNone; @@ -856,37 +836,54 @@ Error Diags::ProcessCmd(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_ if (aArgsLength == 0) { - snprintf(aOutput, aOutputMaxLen, "diagnostics mode is %s\r\n", otPlatDiagModeGet() ? "enabled" : "disabled"); + Output("diagnostics mode is %s\r\n", otPlatDiagModeGet() ? "enabled" : "disabled"); ExitNow(); } - else - { - aOutput[0] = '\0'; - } for (const Command &command : sCommands) { if (strcmp(aArgs[0], command.mName) == 0) { - error = (this->*command.mCommand)(aArgsLength - 1, (aArgsLength > 1) ? &aArgs[1] : nullptr, aOutput, - aOutputMaxLen); + error = (this->*command.mCommand)(aArgsLength - 1, (aArgsLength > 1) ? &aArgs[1] : nullptr); ExitNow(); } } // more platform specific features will be processed under platform layer - error = otPlatDiagProcess(&GetInstance(), aArgsLength, aArgs, aOutput, aOutputMaxLen); + error = otPlatDiagProcess(&GetInstance(), aArgsLength, aArgs); exit: // Add more platform specific diagnostics features here. if (error == kErrorInvalidCommand && aArgsLength > 1) { - snprintf(aOutput, aOutputMaxLen, "diag feature '%s' is not supported\r\n", aArgs[0]); + Output("diag feature '%s' is not supported\r\n", aArgs[0]); } return error; } +void Diags::SetOutputCallback(otDiagOutputCallback aCallback, void *aContext) +{ + mOutputCallback = aCallback; + mOutputContext = aContext; + + otPlatDiagSetOutputCallback(&GetInstance(), aCallback, aContext); +} + +void Diags::Output(const char *aFormat, ...) +{ + va_list args; + + va_start(args, aFormat); + + if (mOutputCallback != nullptr) + { + mOutputCallback(aFormat, args, mOutputContext); + } + + va_end(args); +} + bool Diags::IsEnabled(void) { return otPlatDiagModeGet(); } } // namespace FactoryDiags @@ -986,4 +983,5 @@ OT_TOOL_WEAK otError otPlatDiagRadioGetPowerSettings(otInstance *aInstance, return OT_ERROR_NOT_IMPLEMENTED; } + #endif // OPENTHREAD_CONFIG_DIAG_ENABLE diff --git a/src/core/diags/factory_diags.hpp b/src/core/diags/factory_diags.hpp index 1b72a15639ec..be35bd2bda47 100644 --- a/src/core/diags/factory_diags.hpp +++ b/src/core/diags/factory_diags.hpp @@ -40,6 +40,7 @@ #include +#include #include #include "common/clearable.hpp" @@ -70,7 +71,7 @@ class Diags : public InstanceLocator, private NonCopyable * @param[in] aOutputMaxLen The output buffer size. * */ - Error ProcessLine(const char *aString, char *aOutput, size_t aOutputMaxLen); + Error ProcessLine(const char *aString); /** * Processes a factory diagnostics command line. @@ -85,7 +86,7 @@ class Diags : public InstanceLocator, private NonCopyable * @retval kErrorNotImplemented The command is not supported. * */ - Error ProcessCmd(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen); + Error ProcessCmd(uint8_t aArgsLength, char *aArgs[]); /** * Indicates whether or not the factory diagnostics mode is enabled. @@ -123,13 +124,22 @@ class Diags : public InstanceLocator, private NonCopyable */ void TransmitDone(Error aError); + /** + * Sets the diag output callback. + * + * @param[in] aCallback A callback method called to output diag messages. + * @param[in] aContext A user context pointer. + * + */ + void SetOutputCallback(otDiagOutputCallback aCallback, void *aContext); + private: static constexpr uint8_t kMaxArgs = OPENTHREAD_CONFIG_DIAG_CMD_LINE_ARGS_MAX; struct Command { const char *mName; - Error (Diags::*mCommand)(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen); + Error (Diags::*mCommand)(uint8_t aArgsLength, char *aArgs[]); }; struct Stats : public Clearable @@ -180,29 +190,30 @@ class Diags : public InstanceLocator, private NonCopyable }; Error ParseCmd(char *aString, uint8_t &aArgsLength, char *aArgs[]); - Error ProcessChannel(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen); - Error ProcessContinuousWave(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen); - Error ProcessGpio(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen); - Error ProcessPower(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen); - Error ProcessRadio(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen); - Error ProcessRepeat(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen); - Error ProcessPowerSettings(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen); - Error ProcessRawPowerSetting(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen); - Error ProcessSend(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen); - Error ProcessStart(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen); - Error ProcessStats(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen); - Error ProcessStop(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen); - Error ProcessStream(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen); + Error ProcessChannel(uint8_t aArgsLength, char *aArgs[]); + Error ProcessContinuousWave(uint8_t aArgsLength, char *aArgs[]); + Error ProcessGpio(uint8_t aArgsLength, char *aArgs[]); + Error ProcessPower(uint8_t aArgsLength, char *aArgs[]); + Error ProcessRadio(uint8_t aArgsLength, char *aArgs[]); + Error ProcessRepeat(uint8_t aArgsLength, char *aArgs[]); + Error ProcessPowerSettings(uint8_t aArgsLength, char *aArgs[]); + Error ProcessRawPowerSetting(uint8_t aArgsLength, char *aArgs[]); + Error ProcessSend(uint8_t aArgsLength, char *aArgs[]); + Error ProcessStart(uint8_t aArgsLength, char *aArgs[]); + Error ProcessStats(uint8_t aArgsLength, char *aArgs[]); + Error ProcessStop(uint8_t aArgsLength, char *aArgs[]); + Error ProcessStream(uint8_t aArgsLength, char *aArgs[]); #if OPENTHREAD_RADIO && !OPENTHREAD_RADIO_CLI - Error ProcessEcho(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen); + Error ProcessEcho(uint8_t aArgsLength, char *aArgs[]); #endif Error GetRawPowerSetting(RawPowerSetting &aRawPowerSetting); Error GetPowerSettings(uint8_t aChannel, PowerSettings &aPowerSettings); void TransmitPacket(void); + void Output(const char *aFormat, ...); + void AppendErrorResult(Error aError); - static void AppendErrorResult(Error aError, char *aOutput, size_t aOutputMaxLen); static Error ParseLong(char *aString, long &aLong); static Error ParseBool(char *aString, bool &aBool); @@ -220,6 +231,9 @@ class Diags : public InstanceLocator, private NonCopyable bool mRepeatActive; bool mDiagSendOn; #endif + + otDiagOutputCallback mOutputCallback; + void *mOutputContext; }; } // namespace FactoryDiags diff --git a/src/lib/spinel/radio_spinel.cpp b/src/lib/spinel/radio_spinel.cpp index 6cef385659cf..605d1debaa1f 100644 --- a/src/lib/spinel/radio_spinel.cpp +++ b/src/lib/spinel/radio_spinel.cpp @@ -99,8 +99,8 @@ RadioSpinel::RadioSpinel(void) #endif #if OPENTHREAD_CONFIG_DIAG_ENABLE , mDiagMode(false) - , mDiagOutput(nullptr) - , mDiagOutputMaxLen(0) + , mOutputCallback(nullptr) + , mOutputContext(nullptr) #endif , mTxRadioEndUs(UINT64_MAX) , mRadioTimeRecalcStart(UINT64_MAX) @@ -431,12 +431,13 @@ void RadioSpinel::HandleWaitingResponse(uint32_t aCommand, else if (aKey == SPINEL_PROP_NEST_STREAM_MFG) { spinel_ssize_t unpacked; + const char *diagOutput; mError = OT_ERROR_NONE; - EXPECT(mDiagOutput != nullptr, NO_ACTION); - unpacked = - spinel_datatype_unpack_in_place(aBuffer, aLength, SPINEL_DATATYPE_UTF8_S, mDiagOutput, &mDiagOutputMaxLen); + EXPECT(mOutputCallback != nullptr, NO_ACTION); + unpacked = spinel_datatype_unpack(aBuffer, aLength, SPINEL_DATATYPE_UTF8_S, &diagOutput); EXPECT(unpacked > 0, mError = OT_ERROR_PARSE); + PlatDiagOutput("%s", diagOutput); } #endif else if (aKey == mWaitingKey) @@ -591,6 +592,17 @@ void RadioSpinel::HandleValueIs(spinel_prop_key_t aKey, const uint8_t *aBuffer, break; } } +#if OPENTHREAD_CONFIG_DIAG_ENABLE + else if (aKey == SPINEL_PROP_NEST_STREAM_MFG) + { + const char *diagOutput; + + EXPECT(mOutputCallback != nullptr, NO_ACTION); + unpacked = spinel_datatype_unpack(aBuffer, aLength, SPINEL_DATATYPE_UTF8_S, &diagOutput); + EXPECT(unpacked > 0, error = OT_ERROR_PARSE); + PlatDiagOutput("%s", diagOutput); + } +#endif #if OPENTHREAD_SPINEL_CONFIG_VENDOR_HOOK_ENABLE else if (aKey >= SPINEL_PROP_VENDOR__BEGIN && aKey < SPINEL_PROP_VENDOR__END) { @@ -1753,20 +1765,31 @@ otError RadioSpinel::Disable(void) } #if OPENTHREAD_CONFIG_DIAG_ENABLE -otError RadioSpinel::PlatDiagProcess(const char *aString, char *aOutput, size_t aOutputMaxLen) +void RadioSpinel::SetDiagOutputCallback(otPlatDiagOutputCallback aCallback, void *aContext) { - otError error; + mOutputCallback = aCallback; + mOutputContext = aContext; +} - mDiagOutput = aOutput; - mDiagOutputMaxLen = aOutputMaxLen; +otError RadioSpinel::PlatDiagProcess(const char *aString) +{ + return Set(SPINEL_PROP_NEST_STREAM_MFG, SPINEL_DATATYPE_UTF8_S, aString); +} - error = Set(SPINEL_PROP_NEST_STREAM_MFG, SPINEL_DATATYPE_UTF8_S, aString); +void RadioSpinel::PlatDiagOutput(const char *aFormat, ...) +{ + va_list args; - mDiagOutput = nullptr; - mDiagOutputMaxLen = 0; + va_start(args, aFormat); - return error; + if (mOutputCallback != nullptr) + { + mOutputCallback(aFormat, args, mOutputContext); + } + + va_end(args); } + #endif uint32_t RadioSpinel::GetRadioChannelMask(bool aPreferred) diff --git a/src/lib/spinel/radio_spinel.hpp b/src/lib/spinel/radio_spinel.hpp index 8fdca562ef3d..4d30ff893fce 100644 --- a/src/lib/spinel/radio_spinel.hpp +++ b/src/lib/spinel/radio_spinel.hpp @@ -34,6 +34,7 @@ #ifndef RADIO_SPINEL_HPP_ #define RADIO_SPINEL_HPP_ +#include #include #include "openthread-spinel-config.h" @@ -678,15 +679,22 @@ class RadioSpinel : private Logger * Processes platform diagnostics commands. * * @param[in] aString A null-terminated input string. - * @param[out] aOutput The diagnostics execution result. - * @param[in] aOutputMaxLen The output buffer size. * * @retval OT_ERROR_NONE Succeeded. * @retval OT_ERROR_BUSY Failed due to another operation is on going. * @retval OT_ERROR_RESPONSE_TIMEOUT Failed due to no response received from the transceiver. * */ - otError PlatDiagProcess(const char *aString, char *aOutput, size_t aOutputMaxLen); + otError PlatDiagProcess(const char *aString); + + /** + * Sets the diag output callback. + * + * @param[in] aCallback A pointer to a function that is called on outputting diag messages. + * @param[in] aContext A pointer to the user context. + * + */ + void SetDiagOutputCallback(otPlatDiagOutputCallback aCallback, void *aContext); #endif /** @@ -1194,6 +1202,10 @@ class RadioSpinel : private Logger static otError ReadMacKey(const otMacKeyMaterial &aKeyMaterial, otMacKey &aKey); #endif +#if OPENTHREAD_CONFIG_DIAG_ENABLE + void PlatDiagOutput(const char *aFormat, ...); +#endif + otInstance *mInstance; RadioSpinelCallbacks mCallbacks; ///< Callbacks for notifications of higher layer. @@ -1281,9 +1293,9 @@ class RadioSpinel : private Logger #endif // OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0 #if OPENTHREAD_CONFIG_DIAG_ENABLE - bool mDiagMode; - char *mDiagOutput; - size_t mDiagOutputMaxLen; + bool mDiagMode; + otPlatDiagOutputCallback mOutputCallback; + void *mOutputContext; #endif uint64_t mTxRadioEndUs; diff --git a/src/ncp/ncp_base.cpp b/src/ncp/ncp_base.cpp index 6b99b4d4a71c..a256f45bc490 100644 --- a/src/ncp/ncp_base.cpp +++ b/src/ncp/ncp_base.cpp @@ -313,6 +313,10 @@ NcpBase::NcpBase(Instance *aInstance) , mTxSpinelFrameCounter(0) , mDidInitialUpdates(false) , mLogTimestampBase(0) +#if OPENTHREAD_CONFIG_DIAG_ENABLE + , mDiagOutput(nullptr) + , mDiagOutputLen(0) +#endif { OT_ASSERT(mInstance != nullptr); @@ -354,6 +358,9 @@ NcpBase::NcpBase(Instance *aInstance) otSrpClientSetCallback(mInstance, HandleSrpClientCallback, this); #endif #endif // OPENTHREAD_MTD || OPENTHREAD_FTD +#if OPENTHREAD_CONFIG_DIAG_ENABLE + otDiagSetOutputCallback(mInstance, &NcpBase::HandleDiagOutput_Jump, this); +#endif mChangedPropsSet.AddLastStatus(SPINEL_STATUS_RESET_UNKNOWN); mUpdateChangedPropsTask.Post(); @@ -1418,12 +1425,13 @@ otError NcpBase::CommandHandler_POKE(uint8_t aHeader) // ---------------------------------------------------------------------------- #if OPENTHREAD_CONFIG_DIAG_ENABLE +#include otError NcpBase::HandlePropertySet_SPINEL_PROP_NEST_STREAM_MFG(uint8_t aHeader) { - const char *string = nullptr; - char output[OPENTHREAD_CONFIG_DIAG_OUTPUT_BUFFER_SIZE]; - otError error = OT_ERROR_NONE; + const char *string = nullptr; + char output[OPENTHREAD_CONFIG_DIAG_OUTPUT_BUFFER_SIZE] = {0}; + otError error = OT_ERROR_NONE; error = mDecoder.ReadUtf8(string); @@ -1438,17 +1446,61 @@ otError NcpBase::HandlePropertySet_SPINEL_PROP_NEST_STREAM_MFG(uint8_t aHeader) } #endif - SuccessOrExit(error = otDiagProcessCmdLine(mInstance, string, output, sizeof(output))); + mDiagOutput = output; + mDiagOutputLen = sizeof(output); + + SuccessOrExit(error = otDiagProcessCmdLine(mInstance, string)); // Prepare the response SuccessOrExit(error = mEncoder.BeginFrame(aHeader, SPINEL_CMD_PROP_VALUE_IS, SPINEL_PROP_NEST_STREAM_MFG)); SuccessOrExit(error = mEncoder.WriteUtf8(output)); SuccessOrExit(error = mEncoder.EndFrame()); + otLogNotePlat("HandlePropertySet_SPINEL_PROP_NEST_STREAM_MFG() output=%s", output); + exit: + mDiagOutput = nullptr; + mDiagOutputLen = 0; + return error; } +void NcpBase::HandleDiagOutput_Jump(const char *aFormat, va_list aArguments, void *aContext) +{ + static_cast(aContext)->HandleDiagOutput(aFormat, aArguments); +} + +void NcpBase::HandleDiagOutput(const char *aFormat, va_list aArguments) +{ + int charsWritten; + + if (mDiagOutput != nullptr) + { + charsWritten = vsnprintf(mDiagOutput, mDiagOutputLen, aFormat, aArguments); + VerifyOrExit(charsWritten > 0); + charsWritten = (mDiagOutputLen <= charsWritten) ? mDiagOutputLen : charsWritten; + mDiagOutput += charsWritten; + mDiagOutputLen -= charsWritten; + otLogNotePlat("HandleDiagOutput() 1 charsWritten=%d", charsWritten); + } + else + { + uint8_t header = SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0; + char output[OPENTHREAD_CONFIG_DIAG_OUTPUT_BUFFER_SIZE]; + + charsWritten = vsnprintf(output, sizeof(output), aFormat, aArguments); + VerifyOrExit(charsWritten >= 0); + + SuccessOrExit(mEncoder.BeginFrame(header, SPINEL_CMD_PROP_VALUE_IS, SPINEL_PROP_NEST_STREAM_MFG)); + SuccessOrExit(mEncoder.WriteUtf8(output)); + SuccessOrExit(mEncoder.EndFrame()); + otLogNotePlat("HandleDiagOutput() 2 charsWritten=%d", charsWritten); + } + +exit: + return; +} + #endif // OPENTHREAD_CONFIG_DIAG_ENABLE template <> otError NcpBase::HandlePropertyGet(void) diff --git a/src/ncp/ncp_base.hpp b/src/ncp/ncp_base.hpp index 676b7b3ba580..831705791d7a 100644 --- a/src/ncp/ncp_base.hpp +++ b/src/ncp/ncp_base.hpp @@ -549,6 +549,11 @@ class NcpBase static uint8_t ConvertLogLevel(otLogLevel aLogLevel); static unsigned int ConvertLogRegion(otLogRegion aLogRegion); +#if OPENTHREAD_CONFIG_DIAG_ENABLE + static void HandleDiagOutput_Jump(const char *aFormat, va_list aArguments, void *aContext); + void HandleDiagOutput(const char *aFormat, va_list aArguments); +#endif + #if OPENTHREAD_ENABLE_NCP_VENDOR_HOOK /** * Defines a vendor "command handler" hook to process vendor-specific spinel commands. @@ -734,6 +739,11 @@ class NcpBase bool mDidInitialUpdates; uint64_t mLogTimestampBase; // Timestamp base used for logging + +#if OPENTHREAD_CONFIG_DIAG_ENABLE + char *mDiagOutput; + uint16_t mDiagOutputLen; +#endif }; } // namespace Ncp diff --git a/src/posix/platform/radio.cpp b/src/posix/platform/radio.cpp index eabeaf6bb79a..ba63d1b8d6ca 100644 --- a/src/posix/platform/radio.cpp +++ b/src/posix/platform/radio.cpp @@ -498,11 +498,55 @@ otError otPlatRadioGetCoexMetrics(otInstance *aInstance, otRadioCoexMetrics *aCo #endif #if OPENTHREAD_CONFIG_DIAG_ENABLE -otError otPlatDiagProcess(otInstance *aInstance, - uint8_t aArgsLength, - char *aArgs[], - char *aOutput, - size_t aOutputMaxLen) +static otPlatDiagOutputCallback sDiagOutputCallback = nullptr; +static void *sDiagCallbackContext = nullptr; +static char *sDiagOutput = nullptr; +static uint16_t sDiagOutputLen = 0; + +static void handleDiagOutput(const char *aFormat, va_list aArguments, void *aContext) +{ + OT_UNUSED_VARIABLE(aContext); + int charsWritten; + + VerifyOrExit((sDiagOutput != nullptr) && (sDiagOutputLen > 0)); + charsWritten = vsnprintf(sDiagOutput, sDiagOutputLen, aFormat, aArguments); + VerifyOrExit(charsWritten > 0); + charsWritten = (sDiagOutputLen <= charsWritten) ? sDiagOutputLen : charsWritten; + sDiagOutput += charsWritten; + sDiagOutputLen -= charsWritten; + +exit: + return; +} + +static void setDiagOutput(char *aOutput, size_t aSize) +{ + sDiagOutput = aOutput; + sDiagOutputLen = static_cast(aSize); + GetRadioSpinel().SetDiagOutputCallback(handleDiagOutput, nullptr); +} + +static void freeDiagOutput(void) +{ + sDiagOutput = nullptr; + sDiagOutputLen = 0; + GetRadioSpinel().SetDiagOutputCallback(sDiagOutputCallback, sDiagCallbackContext); +} + +void otPlatDiagSetOutputCallback(otInstance *aInstance, otPlatDiagOutputCallback aCallback, void *aContext) +{ + OT_UNUSED_VARIABLE(aInstance); + + sDiagOutputCallback = aCallback; + sDiagCallbackContext = aContext; + + GetRadioSpinel().SetDiagOutputCallback(aCallback, aContext); +#if OPENTHREAD_POSIX_CONFIG_RCP_CAPS_DIAG_ENABLE + GetRcpCapsDiag().SetDiagOutputCallback(aCallback, aContext); +#endif +} + +otError otPlatDiagProcess(otInstance *aInstance, uint8_t aArgsLength, char *aArgs[]) { // deliver the platform specific diags commands to radio only ncp. OT_UNUSED_VARIABLE(aInstance); @@ -513,7 +557,7 @@ otError otPlatDiagProcess(otInstance *aInstance, #if OPENTHREAD_POSIX_CONFIG_RCP_CAPS_DIAG_ENABLE if (strcmp(aArgs[0], "rcpcaps") == 0) { - return GetRcpCapsDiag().DiagProcess(aArgs, aArgsLength, aOutput, aOutputMaxLen); + return GetRcpCapsDiag().DiagProcess(aArgs, aArgsLength); } #endif @@ -522,12 +566,12 @@ otError otPlatDiagProcess(otInstance *aInstance, cur += snprintf(cur, static_cast(end - cur), "%s ", aArgs[index]); } - return GetRadioSpinel().PlatDiagProcess(cmd, aOutput, aOutputMaxLen); + return GetRadioSpinel().PlatDiagProcess(cmd); } void otPlatDiagModeSet(bool aMode) { - SuccessOrExit(GetRadioSpinel().PlatDiagProcess(aMode ? "start" : "stop", nullptr, 0)); + SuccessOrExit(GetRadioSpinel().PlatDiagProcess(aMode ? "start" : "stop")); GetRadioSpinel().SetDiagEnabled(aMode); exit: @@ -541,7 +585,7 @@ void otPlatDiagTxPowerSet(int8_t aTxPower) char cmd[OPENTHREAD_CONFIG_DIAG_CMD_LINE_BUFFER_SIZE]; snprintf(cmd, sizeof(cmd), "power %d", aTxPower); - SuccessOrExit(GetRadioSpinel().PlatDiagProcess(cmd, nullptr, 0)); + SuccessOrExit(GetRadioSpinel().PlatDiagProcess(cmd)); exit: return; @@ -552,7 +596,7 @@ void otPlatDiagChannelSet(uint8_t aChannel) char cmd[OPENTHREAD_CONFIG_DIAG_CMD_LINE_BUFFER_SIZE]; snprintf(cmd, sizeof(cmd), "channel %d", aChannel); - SuccessOrExit(GetRadioSpinel().PlatDiagProcess(cmd, nullptr, 0)); + SuccessOrExit(GetRadioSpinel().PlatDiagProcess(cmd)); exit: return; @@ -564,7 +608,7 @@ otError otPlatDiagGpioSet(uint32_t aGpio, bool aValue) char cmd[OPENTHREAD_CONFIG_DIAG_CMD_LINE_BUFFER_SIZE]; snprintf(cmd, sizeof(cmd), "gpio set %d %d", aGpio, aValue); - SuccessOrExit(error = GetRadioSpinel().PlatDiagProcess(cmd, nullptr, 0)); + SuccessOrExit(error = GetRadioSpinel().PlatDiagProcess(cmd)); exit: return error; @@ -577,12 +621,16 @@ otError otPlatDiagGpioGet(uint32_t aGpio, bool *aValue) char output[OPENTHREAD_CONFIG_DIAG_OUTPUT_BUFFER_SIZE]; char *str; + setDiagOutput(output, sizeof(output)); + snprintf(cmd, sizeof(cmd), "gpio get %d", aGpio); - SuccessOrExit(error = GetRadioSpinel().PlatDiagProcess(cmd, output, sizeof(output))); + SuccessOrExit(error = GetRadioSpinel().PlatDiagProcess(cmd)); VerifyOrExit((str = strtok(output, "\r")) != nullptr, error = OT_ERROR_FAILED); *aValue = static_cast(atoi(str)); exit: + freeDiagOutput(); + return error; } @@ -592,7 +640,7 @@ otError otPlatDiagGpioSetMode(uint32_t aGpio, otGpioMode aMode) char cmd[OPENTHREAD_CONFIG_DIAG_CMD_LINE_BUFFER_SIZE]; snprintf(cmd, sizeof(cmd), "gpio mode %d %s", aGpio, aMode == OT_GPIO_MODE_INPUT ? "in" : "out"); - SuccessOrExit(error = GetRadioSpinel().PlatDiagProcess(cmd, nullptr, 0)); + SuccessOrExit(error = GetRadioSpinel().PlatDiagProcess(cmd)); exit: return error; @@ -605,8 +653,10 @@ otError otPlatDiagGpioGetMode(uint32_t aGpio, otGpioMode *aMode) char output[OPENTHREAD_CONFIG_DIAG_OUTPUT_BUFFER_SIZE]; char *str; + setDiagOutput(output, sizeof(output)); + snprintf(cmd, sizeof(cmd), "gpio mode %d", aGpio); - SuccessOrExit(error = GetRadioSpinel().PlatDiagProcess(cmd, output, sizeof(output))); + SuccessOrExit(error = GetRadioSpinel().PlatDiagProcess(cmd)); VerifyOrExit((str = strtok(output, "\r")) != nullptr, error = OT_ERROR_FAILED); if (strcmp(str, "in") == 0) @@ -623,6 +673,8 @@ otError otPlatDiagGpioGetMode(uint32_t aGpio, otGpioMode *aMode) } exit: + freeDiagOutput(); + return error; } @@ -648,8 +700,10 @@ otError otPlatDiagRadioGetPowerSettings(otInstance *aInstance, assert((aTargetPower != nullptr) && (aActualPower != nullptr) && (aRawPowerSetting != nullptr) && (aRawPowerSettingLength != nullptr)); + setDiagOutput(output, sizeof(output)); + snprintf(cmd, sizeof(cmd), "powersettings %d", aChannel); - SuccessOrExit(error = GetRadioSpinel().PlatDiagProcess(cmd, output, sizeof(output))); + SuccessOrExit(error = GetRadioSpinel().PlatDiagProcess(cmd)); snprintf(fmt, sizeof(fmt), "TargetPower(0.01dBm): %%d\r\nActualPower(0.01dBm): %%d\r\nRawPowerSetting: %%%us\r\n", kRawPowerStringSize); VerifyOrExit(sscanf(output, fmt, &targetPower, &actualPower, rawPowerSetting) == 3, error = OT_ERROR_FAILED); @@ -659,6 +713,8 @@ otError otPlatDiagRadioGetPowerSettings(otInstance *aInstance, *aActualPower = static_cast(actualPower); exit: + freeDiagOutput(); + return error; } @@ -682,7 +738,7 @@ otError otPlatDiagRadioSetRawPowerSetting(otInstance *aInstance, VerifyOrExit(nbytes < static_cast(sizeof(cmd)), error = OT_ERROR_INVALID_ARGS); } - SuccessOrExit(error = GetRadioSpinel().PlatDiagProcess(cmd, nullptr, 0)); + SuccessOrExit(error = GetRadioSpinel().PlatDiagProcess(cmd)); exit: return error; @@ -700,12 +756,16 @@ otError otPlatDiagRadioGetRawPowerSetting(otInstance *aInstance, assert((aRawPowerSetting != nullptr) && (aRawPowerSettingLength != nullptr)); + setDiagOutput(output, sizeof(output)); + snprintf(cmd, sizeof(cmd), "rawpowersetting"); - SuccessOrExit(error = GetRadioSpinel().PlatDiagProcess(cmd, output, sizeof(output))); + SuccessOrExit(error = GetRadioSpinel().PlatDiagProcess(cmd)); VerifyOrExit((str = strtok(output, "\r")) != nullptr, error = OT_ERROR_FAILED); SuccessOrExit(error = ot::Utils::CmdLineParser::ParseAsHexString(str, *aRawPowerSettingLength, aRawPowerSetting)); exit: + freeDiagOutput(); + return error; } @@ -717,7 +777,7 @@ otError otPlatDiagRadioRawPowerSettingEnable(otInstance *aInstance, bool aEnable char cmd[OPENTHREAD_CONFIG_DIAG_CMD_LINE_BUFFER_SIZE]; snprintf(cmd, sizeof(cmd), "rawpowersetting %s", aEnable ? "enable" : "disable"); - SuccessOrExit(error = GetRadioSpinel().PlatDiagProcess(cmd, nullptr, 0)); + SuccessOrExit(error = GetRadioSpinel().PlatDiagProcess(cmd)); exit: return error; @@ -731,7 +791,7 @@ otError otPlatDiagRadioTransmitCarrier(otInstance *aInstance, bool aEnable) char cmd[OPENTHREAD_CONFIG_DIAG_CMD_LINE_BUFFER_SIZE]; snprintf(cmd, sizeof(cmd), "cw %s", aEnable ? "start" : "stop"); - SuccessOrExit(error = GetRadioSpinel().PlatDiagProcess(cmd, nullptr, 0)); + SuccessOrExit(error = GetRadioSpinel().PlatDiagProcess(cmd)); exit: return error; @@ -744,7 +804,7 @@ otError otPlatDiagRadioTransmitStream(otInstance *aInstance, bool aEnable) char cmd[OPENTHREAD_CONFIG_DIAG_CMD_LINE_BUFFER_SIZE]; snprintf(cmd, sizeof(cmd), "stream %s", aEnable ? "start" : "stop"); - return GetRadioSpinel().PlatDiagProcess(cmd, nullptr, 0); + return GetRadioSpinel().PlatDiagProcess(cmd); } void otPlatDiagRadioReceived(otInstance *aInstance, otRadioFrame *aFrame, otError aError) diff --git a/src/posix/platform/rcp_caps_diag.cpp b/src/posix/platform/rcp_caps_diag.cpp index 79959b210354..a6fcd07838db 100644 --- a/src/posix/platform/rcp_caps_diag.cpp +++ b/src/posix/platform/rcp_caps_diag.cpp @@ -86,15 +86,12 @@ const struct RcpCapsDiag::SpinelEntry RcpCapsDiag::sSpinelEntries[] = { SPINEL_ENTRY(kCategoryOptional, SPINEL_CMD_PROP_VALUE_GET, SPINEL_PROP_PHY_CCA_THRESHOLD), }; -otError RcpCapsDiag::DiagProcess(char *aArgs[], uint8_t aArgsLength, char *aOutput, size_t aOutputMaxLen) +otError RcpCapsDiag::DiagProcess(char *aArgs[], uint8_t aArgsLength) { otError error = OT_ERROR_NONE; VerifyOrExit(aArgsLength == 2, error = OT_ERROR_INVALID_ARGS); - mOutputStart = aOutput; - mOutputEnd = aOutput + aOutputMaxLen; - if (strcmp(aArgs[1], "spinel") == 0) { ProcessSpinel(); @@ -104,9 +101,6 @@ otError RcpCapsDiag::DiagProcess(char *aArgs[], uint8_t aArgsLength, char *aOutp error = OT_ERROR_INVALID_COMMAND; } - mOutputStart = nullptr; - mOutputEnd = nullptr; - exit: return error; } @@ -137,6 +131,12 @@ void RcpCapsDiag::TestSpinelCommands(Category aCategory) } } +void RcpCapsDiag::SetDiagOutputCallback(otPlatDiagOutputCallback aCallback, void *aContext) +{ + mOutputCallback = aCallback; + mOutputContext = aContext; +} + void RcpCapsDiag::OutputResult(const SpinelEntry &aEntry, otError error) { static constexpr uint8_t kSpaceLength = 1; @@ -161,9 +161,9 @@ void RcpCapsDiag::Output(const char *aFormat, ...) va_start(args, aFormat); - if ((mOutputStart != nullptr) && (mOutputEnd != nullptr) && (mOutputStart < mOutputEnd)) + if (mOutputCallback != nullptr) { - mOutputStart += vsnprintf(mOutputStart, static_cast(mOutputEnd - mOutputStart), aFormat, args); + mOutputCallback(aFormat, args, mOutputContext); } va_end(args); diff --git a/src/posix/platform/rcp_caps_diag.hpp b/src/posix/platform/rcp_caps_diag.hpp index 401c0e72f7e4..ec6d789325ea 100644 --- a/src/posix/platform/rcp_caps_diag.hpp +++ b/src/posix/platform/rcp_caps_diag.hpp @@ -37,6 +37,8 @@ #include "platform-posix.h" #if OPENTHREAD_POSIX_CONFIG_RCP_CAPS_DIAG_ENABLE +#include + #include "lib/spinel/radio_spinel.hpp" #include "lib/spinel/spinel.h" @@ -58,8 +60,8 @@ class RcpCapsDiag */ explicit RcpCapsDiag(Spinel::RadioSpinel &aRadioSpinel) : mRadioSpinel(aRadioSpinel) - , mOutputStart(nullptr) - , mOutputEnd(nullptr) + , mOutputCallback(nullptr) + , mOutputContext(nullptr) { } @@ -68,15 +70,22 @@ class RcpCapsDiag * * @param[in] aArgs The arguments of diagnostics command line. * @param[in] aArgsLength The number of arguments in @p aArgs. - * @param[out] aOutput The diagnostics execution result. - * @param[in] aOutputMaxLen The output buffer size. * * @retval OT_ERROR_INVALID_ARGS The command is supported but invalid arguments provided. * @retval OT_ERROR_NONE The command is successfully processed. * @retval OT_ERROR_INVALID_COMMAND The command is not valid or not supported. * */ - otError DiagProcess(char *aArgs[], uint8_t aArgsLength, char *aOutput, size_t aOutputMaxLen); + otError DiagProcess(char *aArgs[], uint8_t aArgsLength); + + /** + * Sets the diag output callback. + * + * @param[in] aCallback A pointer to a function that is called on outputting diag messages. + * @param[in] aContext A user context pointer. + * + */ + void SetDiagOutputCallback(otPlatDiagOutputCallback aCallback, void *aContext); private: template otError HandleSpinelCommand(void); @@ -108,9 +117,9 @@ class RcpCapsDiag static const struct SpinelEntry sSpinelEntries[]; - Spinel::RadioSpinel &mRadioSpinel; - char *mOutputStart; - char *mOutputEnd; + Spinel::RadioSpinel &mRadioSpinel; + otPlatDiagOutputCallback mOutputCallback; + void *mOutputContext; }; } // namespace Posix diff --git a/tests/fuzz/fuzzer_platform.cpp b/tests/fuzz/fuzzer_platform.cpp index abc8baae3560..524760954b41 100644 --- a/tests/fuzz/fuzzer_platform.cpp +++ b/tests/fuzz/fuzzer_platform.cpp @@ -488,17 +488,18 @@ otError otPlatSettingsDelete(otInstance *aInstance, uint16_t aKey, int aIndex) void otPlatSettingsWipe(otInstance *aInstance) { OT_UNUSED_VARIABLE(aInstance); } -otError otPlatDiagProcess(otInstance *aInstance, - uint8_t aArgsLength, - char *aArgs[], - char *aOutput, - size_t aOutputMaxLen) +void otPlatDiagSetOutputCallback(otInstance *aInstance, otPlatDiagOutputCallback aCallback, void *aContext) +{ + OT_UNUSED_VARIABLE(aInstance); + OT_UNUSED_VARIABLE(aCallback); + OT_UNUSED_VARIABLE(aContext); +} + +otError otPlatDiagProcess(otInstance *aInstance, uint8_t aArgsLength, char *aArgs[]) { OT_UNUSED_VARIABLE(aInstance); OT_UNUSED_VARIABLE(aArgsLength); OT_UNUSED_VARIABLE(aArgs); - OT_UNUSED_VARIABLE(aOutput); - OT_UNUSED_VARIABLE(aOutputMaxLen); return OT_ERROR_INVALID_COMMAND; } diff --git a/tests/unit/test_platform.cpp b/tests/unit/test_platform.cpp index 0cef5796e0dc..89dfda775f11 100644 --- a/tests/unit/test_platform.cpp +++ b/tests/unit/test_platform.cpp @@ -99,6 +99,9 @@ void testFreeInstance(otInstance *aInstance) bool sDiagMode = false; +static otPlatDiagOutputCallback sOutputCallback = nullptr; +static void *sOutputCallbackContext = nullptr; + extern "C" { #if OPENTHREAD_CONFIG_HEAP_EXTERNAL_ENABLE @@ -225,9 +228,30 @@ OT_TOOL_WEAK otError otPlatEntropyGet(uint8_t *aOutput, uint16_t aOutputLength) return error; } -OT_TOOL_WEAK void otPlatDiagProcess(otInstance *, uint8_t, char *aArgs[], char *aOutput, size_t aOutputMaxLen) +static void DiagOutput(const char *aFormat, ...) { - snprintf(aOutput, aOutputMaxLen, "diag feature '%s' is not supported\r\n", aArgs[0]); + va_list args; + + va_start(args, aFormat); + + if (sOutputCallback != nullptr) + { + sOutputCallback(aFormat, args, sOutputCallbackContext); + } + + va_end(args); +} + +OT_TOOL_WEAK void otPlatDiagSetOutputCallback(otInstance *aInstance, otPlatDiagOutputCallback aCallback, void *aContext) +{ + sOutputCallback = aCallback; + sOutputCallbackContext = aContext; +} + +OT_TOOL_WEAK otError otPlatDiagProcess(otInstance *, uint8_t, char *aArgs[]) +{ + DiagOutput("diag feature '%s' is not supported\r\n", aArgs[0]); + return OT_ERROR_NONE; } OT_TOOL_WEAK void otPlatDiagModeSet(bool aMode) { sDiagMode = aMode; } diff --git a/tests/unit/test_platform.h b/tests/unit/test_platform.h index 8f9d2917a215..66df45a5bc6e 100644 --- a/tests/unit/test_platform.h +++ b/tests/unit/test_platform.h @@ -33,6 +33,7 @@ #include #include +#include #include #include #include