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