From 215c23f2a647953f6aafd84d7473040e5fc1b042 Mon Sep 17 00:00:00 2001 From: Zhanglong Xia Date: Thu, 20 Jun 2024 11:27:35 +0800 Subject: [PATCH] [diag] add diag output callback (#10354) 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 ++-- script/check-size | 4 +- script/git-tool | 11 +- src/cli/cli.cpp | 21 ++- src/cli/cli.hpp | 5 + src/core/api/diags_api.cpp | 12 +- src/core/diags/factory_diags.cpp | 259 ++++++++++++++------------- src/core/diags/factory_diags.hpp | 56 +++--- src/lib/spinel/radio_spinel.cpp | 57 ++++-- src/lib/spinel/radio_spinel.hpp | 24 ++- src/ncp/ncp_base.cpp | 56 +++++- 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 + 21 files changed, 505 insertions(+), 269 deletions(-) diff --git a/examples/platforms/simulation/diag.c b/examples/platforms/simulation/diag.c index dcd9f1d9f..8ce6efcfd 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 217abf6cb..9362ef378 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; + +/** + * Sets the diag output callback. + * + * @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. * - * 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] 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 d4325eff0..887c58cb6 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 (422) +#define OPENTHREAD_API_VERSION (423) /** * @addtogroup api-instance diff --git a/include/openthread/platform/diag.h b/include/openthread/platform/diag.h index 7bc229946..888ef448c 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. * - * 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] 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. + * + * @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/script/check-size b/script/check-size index c50ade344..7317b6c37 100755 --- a/script/check-size +++ b/script/check-size @@ -105,9 +105,11 @@ build_nrf52840() case "$2" in new) local sha=${OT_SHA_NEW} + local clone_options=("clone") ;; old) local sha=${OT_SHA_OLD} + local clone_options=("clone" "no-depend") ;; *) exit 128 @@ -119,7 +121,7 @@ build_nrf52840() local config_file="../examples/config/${config_name}" mkdir -p "${OT_TMP_DIR}/${folder}" - script/git-tool clone https://github.com/openthread/ot-nrf528xx.git "${OT_TMP_DIR}/${folder}" + script/git-tool "${clone_options[@]}" https://github.com/openthread/ot-nrf528xx.git "${OT_TMP_DIR}/${folder}" rm -rf "${OT_TMP_DIR}/${folder}/openthread/*" # replace openthread submodule with latest commit git archive "${sha}" | tar x -C "${OT_TMP_DIR}/${folder}/openthread" diff --git a/script/git-tool b/script/git-tool index 4bcdae8e4..c6fb695f0 100755 --- a/script/git-tool +++ b/script/git-tool @@ -80,10 +80,15 @@ try_clone() { local dest_dir - dest_dir="$(git clone "$@" 2>&1 | tee | cut -d\' -f2)" + if [[ $1 == no-depend ]]; then + shift + git clone "$@" 2>&1 + else + dest_dir="$(git clone "$@" 2>&1 | tee | cut -d\' -f2)" - cd "${dest_dir}" - get_pr_body | apply_dependencies + cd "${dest_dir}" + get_pr_body | apply_dependencies + fi } print_help() diff --git a/src/cli/cli.cpp b/src/cli/cli.cpp index 89bb9c8e5..7680a4f58 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 @@ exit: #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 error; + return otDiagProcessCmd(GetInstancePtr(), Arg::GetArgsLength(aArgs), args); } + +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 d5fe30ad9..65af2e839 100644 --- a/src/cli/cli.hpp +++ b/src/cli/cli.hpp @@ -339,6 +339,11 @@ private: #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 a87bde581..cb8488ddc 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 03ce33d72..d5a0ee288 100644 --- a/src/core/diags/factory_diags.cpp +++ b/src/core/diags/factory_diags.cpp @@ -50,17 +50,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; } @@ -85,10 +79,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; @@ -101,11 +97,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; @@ -117,35 +113,39 @@ 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 uint8_t kReservedLen = 1; // 1 byte '\0' + static constexpr uint16_t kOutputLen = OPENTHREAD_CONFIG_DIAG_OUTPUT_BUFFER_SIZE; + static constexpr uint16_t kOutputMaxLen = kOutputLen - kReservedLen; + 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(kOutputMaxLen)); for (i = 0; i < number; i++) { - aOutput[i] = '0' + i % 10; + output[i] = '0' + i % 10; } - snprintf(&aOutput[i], aOutputMaxLen - i, "\r\n"); + output[number] = '\0'; + + Output("%s\r\n", output); } else { @@ -153,28 +153,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); @@ -213,11 +209,13 @@ Diags::Diags(Instance &aInstance) , mIsTxPacketSet(false) , mRepeatActive(false) , mDiagSendOn(false) + , mOutputCallback(nullptr) + , mOutputContext(nullptr) { mStats.Clear(); } -Error Diags::ProcessFrame(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen) +Error Diags::ProcessFrame(uint8_t aArgsLength, char *aArgs[]) { Error error = kErrorNone; uint16_t size = OT_RADIO_FRAME_MAX_SIZE; @@ -231,11 +229,11 @@ Error Diags::ProcessFrame(uint8_t aArgsLength, char *aArgs[], char *aOutput, siz mIsTxPacketSet = true; exit: - AppendErrorResult(error, aOutput, aOutputMaxLen); + AppendErrorResult(error); return error; } -Error Diags::ProcessChannel(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen) +Error Diags::ProcessChannel(uint8_t aArgsLength, char *aArgs[]) { Error error = kErrorNone; @@ -243,7 +241,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 { @@ -256,15 +254,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; @@ -272,7 +270,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 { @@ -284,15 +282,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; @@ -303,7 +301,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 { @@ -335,16 +333,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; @@ -373,16 +371,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); @@ -403,14 +401,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; @@ -419,26 +417,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); @@ -451,17 +448,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; } @@ -483,7 +479,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; @@ -493,7 +489,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) { @@ -502,8 +498,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) { @@ -514,29 +509,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; } @@ -605,7 +600,7 @@ exit: #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; @@ -622,11 +617,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; @@ -643,7 +638,7 @@ Error Diags::ProcessStream(uint8_t aArgsLength, char *aArgs[], char *aOutput, si } exit: - AppendErrorResult(error, aOutput, aOutputMaxLen); + AppendErrorResult(error); return error; } @@ -655,7 +650,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; @@ -668,14 +663,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++) { @@ -683,12 +673,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; } @@ -708,14 +694,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; } @@ -725,7 +710,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; @@ -735,7 +720,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) { @@ -753,11 +738,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; @@ -770,7 +755,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)) { @@ -789,11 +774,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)) @@ -807,15 +792,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); } } @@ -852,7 +837,7 @@ exit: 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; @@ -871,27 +856,26 @@ exit: 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; @@ -907,37 +891,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 diff --git a/src/core/diags/factory_diags.hpp b/src/core/diags/factory_diags.hpp index cb1c7b95b..01864786d 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" @@ -66,26 +67,22 @@ public: * Processes a factory diagnostics command line. * * @param[in] aString A null-terminated input string. - * @param[out] aOutput The diagnostics execution result. - * @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. * * @param[in] aArgsLength The number of args 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 kErrorInvalidArgs The command is supported but invalid arguments provided. * @retval kErrorNone The command is successfully process. * @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 +120,22 @@ public: */ 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,30 +186,31 @@ private: }; Error ParseCmd(char *aString, uint8_t &aArgsLength, char *aArgs[]); - Error ProcessChannel(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen); - Error ProcessFrame(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 ProcessFrame(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); @@ -222,6 +229,9 @@ private: 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 6cef38565..605d1deba 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 @@ exit: } #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; - - mDiagOutput = aOutput; - mDiagOutputMaxLen = aOutputMaxLen; - - error = Set(SPINEL_PROP_NEST_STREAM_MFG, SPINEL_DATATYPE_UTF8_S, aString); - - mDiagOutput = nullptr; - mDiagOutputMaxLen = 0; - - return error; + mOutputCallback = aCallback; + mOutputContext = aContext; } + +otError RadioSpinel::PlatDiagProcess(const char *aString) +{ + return Set(SPINEL_PROP_NEST_STREAM_MFG, SPINEL_DATATYPE_UTF8_S, aString); +} + +void RadioSpinel::PlatDiagOutput(const char *aFormat, ...) +{ + va_list args; + + va_start(args, aFormat); + + 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 8fdca562e..4d30ff893 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 @@ public: * 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 @@ private: 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 @@ private: #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 6b99b4d4a..fc6b56529 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,11 @@ exit: // ---------------------------------------------------------------------------- #if OPENTHREAD_CONFIG_DIAG_ENABLE - 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,7 +1444,10 @@ 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)); @@ -1446,9 +1455,46 @@ otError NcpBase::HandlePropertySet_SPINEL_PROP_NEST_STREAM_MFG(uint8_t aHeader) SuccessOrExit(error = mEncoder.EndFrame()); 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; + } + 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()); + } + +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 676b7b3ba..831705791 100644 --- a/src/ncp/ncp_base.hpp +++ b/src/ncp/ncp_base.hpp @@ -549,6 +549,11 @@ protected: 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 @@ protected: 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 eabeaf6bb..ba63d1b8d 100644 --- a/src/posix/platform/radio.cpp +++ b/src/posix/platform/radio.cpp @@ -498,11 +498,55 @@ exit: #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 e9b9517c8..b0f15e494 100644 --- a/src/posix/platform/rcp_caps_diag.cpp +++ b/src/posix/platform/rcp_caps_diag.cpp @@ -447,15 +447,12 @@ const struct RcpCapsDiag::SpinelEntry RcpCapsDiag::sSpinelEntries[] = { SPINEL_ENTRY(kCategoryUtils, SPINEL_CMD_PROP_VALUE_SET, SPINEL_PROP_RADIO_COEX_ENABLE), }; -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(); @@ -465,9 +462,6 @@ otError RcpCapsDiag::DiagProcess(char *aArgs[], uint8_t aArgsLength, char *aOutp error = OT_ERROR_INVALID_COMMAND; } - mOutputStart = nullptr; - mOutputEnd = nullptr; - exit: return error; } @@ -498,6 +492,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; @@ -522,9 +522,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 1b01f098e..f799f8187 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 @@ public: */ explicit RcpCapsDiag(Spinel::RadioSpinel &aRadioSpinel) : mRadioSpinel(aRadioSpinel) - , mOutputStart(nullptr) - , mOutputEnd(nullptr) + , mOutputCallback(nullptr) + , mOutputContext(nullptr) { } @@ -68,15 +70,22 @@ public: * * @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 @@ private: 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 abc8baae3..524760954 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 0cef5796e..89dfda775 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 @@ exit: 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 8f9d2917a..66df45a5b 100644 --- a/tests/unit/test_platform.h +++ b/tests/unit/test_platform.h @@ -33,6 +33,7 @@ #include #include +#include #include #include #include