[cli] add multi-interpreter support (#13027)

This commit introduces an opaque `otCliInterpreter` type and a set of
new public C CLI APIs (e.g., `otCliInterpreterInit()`,
`otCliInterpreterInputLine()`) to support multiple, dynamically
allocated CLI interpreters per OpenThread instance.

This architecture allows applications to instantiate and manage
multiple concurrent CLI sessions. Backward compatibility is preserved
by retaining the original `otCli*` APIs, which now interact with a
single built-in static interpreter.

The `OPENTHREAD_CONFIG_CLI_STATIC_INTERPRETER_ENABLE` configuration
is also added. It enables support for the static interpreter and is
enabled by default. It can be disabled to save RAM in deployments
that solely use the multi-interpreter APIs.
This commit is contained in:
Abtin Keshavarzian
2026-05-07 16:56:00 -05:00
committed by GitHub
parent 1e784183f4
commit 91d00c5fd1
6 changed files with 248 additions and 38 deletions
+108 -21
View File
@@ -36,6 +36,8 @@
#define OPENTHREAD_CLI_H_
#include <stdarg.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <openthread/error.h>
@@ -47,17 +49,6 @@
extern "C" {
#endif
/**
* Represents a CLI command.
*/
typedef struct otCliCommand
{
const char *mName; ///< A pointer to the command string.
otError (*mCommand)(void *aContext,
uint8_t aArgsLength,
char *aArgs[]); ///< A function pointer to process the command.
} otCliCommand;
/**
* @addtogroup api-cli
*
@@ -68,7 +59,12 @@ typedef struct otCliCommand
*/
/**
* Pointer is called to notify about Console output.
* Opaque type for a CLI interpreter.
*/
typedef struct otCliInterpreter otCliInterpreter;
/**
* Pointer is called to notify about CLI interpreter output.
*
* @param[out] aContext A user context pointer.
* @param[in] aFormat The format string.
@@ -80,7 +76,62 @@ typedef int (*otCliOutputCallback)(void *aContext, const char *aFormat, va_list
OT_TOOL_PRINTF_STYLE_FORMAT_ARG_CHECK(2, 0);
/**
* Initialize the CLI module.
* Gets the size of the CLI interpreter object.
*
* @returns The size of the CLI interpreter object in bytes.
*/
size_t otCliInterpreterGetSize(void);
/**
* Initializes a CLI interpreter.
*
* @param[in] aBuffer A pointer to a memory buffer for the CLI interpreter.
* @param[in] aSize The size of the memory buffer.
* @param[in] aInstance The OpenThread instance structure.
* @param[in] aCallback A callback method called to process CLI output.
* @param[in] aContext A user context pointer.
*
* @returns A pointer to the initialized CLI interpreter, or `NULL` if @p aSize is too small.
*/
otCliInterpreter *otCliInterpreterInit(void *aBuffer,
size_t aSize,
otInstance *aInstance,
otCliOutputCallback aCallback,
void *aContext);
/**
* Configures whether or not the CLI interpreter outputs the prompt string.
*
* Requires `OPENTHREAD_CONFIG_CLI_PROMPT_ENABLE`.
*
* It is enabled by default.
*
* @param[in] aInterpreter A pointer to a CLI interpreter.
* @param[in] aEnable TRUE to enable outputting the prompt, FALSE to disable.
*/
void otCliInterpreterSetPromptConfig(otCliInterpreter *aInterpreter, bool aEnable);
/**
* Feeds input to the CLI interpreter.
*
* @param[in] aInterpreter A pointer to a CLI interpreter.
* @param[in] aLine A pointer to a null-terminated string.
*/
void otCliInterpreterInputLine(otCliInterpreter *aInterpreter, char *aLine);
/**
* Finalizes the CLI interpreter.
*
* @param[in] aInterpreter A pointer to a CLI interpreter.
*/
void otCliInterpreterFinalize(otCliInterpreter *aInterpreter);
//--------------------------------------------------------------------------------------------------------------------
/**
* Initialize the static CLI interpreter.
*
* Requires `OPENTHREAD_CONFIG_CLI_STATIC_INTERPRETER_ENABLE`.
*
* @param[in] aInstance The OpenThread instance structure.
* @param[in] aCallback A callback method called to process CLI output.
@@ -89,14 +140,36 @@ typedef int (*otCliOutputCallback)(void *aContext, const char *aFormat, va_list
void otCliInit(otInstance *aInstance, otCliOutputCallback aCallback, void *aContext);
/**
* Is called to feed in a console input line.
* Gets the pointer to the static CLI interpreter.
*
* @param[in] aBuf A pointer to a null-terminated string.
* Requires `OPENTHREAD_CONFIG_CLI_STATIC_INTERPRETER_ENABLE`.
*
* @returns A pointer to the static CLI interpreter.
*/
void otCliInputLine(char *aBuf);
otCliInterpreter *otCliGetStaticInterpreter(void);
/**
* Set a user command table.
* Feeds input to the static CLI interpreter.
*
* Requires `OPENTHREAD_CONFIG_CLI_STATIC_INTERPRETER_ENABLE`.
*
* @param[in] aLine A pointer to a null-terminated string.
*/
void otCliInputLine(char *aLine);
/**
* Represents a user provided CLI command entry.
*/
typedef struct otCliCommand
{
const char *mName; ///< The command string.
otError (*mCommand)(void *aContext, uint8_t aArgsLength, char *aArgs[]); ///< Command handler function pointer.
} otCliCommand;
/**
* Set a user command table on the static CLI interpreter.
*
* Requires `OPENTHREAD_CONFIG_CLI_STATIC_INTERPRETER_ENABLE`.
*
* @param[in] aUserCommands A pointer to an array with user commands.
* @param[in] aLength The @p aUserCommands length.
@@ -108,7 +181,11 @@ void otCliInputLine(char *aBuf);
otError otCliSetUserCommands(const otCliCommand *aUserCommands, uint8_t aLength, void *aContext);
/**
* Write a number of bytes to the CLI console as a hex string.
* Write a number of bytes to the static CLI interpreter output as a hex string.
*
* Requires `OPENTHREAD_CONFIG_CLI_STATIC_INTERPRETER_ENABLE`.
*
* This is intended for use by user-provided CLI command handlers.
*
* @param[in] aBytes A pointer to data which should be printed.
* @param[in] aLength @p aBytes length.
@@ -116,7 +193,11 @@ otError otCliSetUserCommands(const otCliCommand *aUserCommands, uint8_t aLength,
void otCliOutputBytes(const uint8_t *aBytes, uint8_t aLength);
/**
* Write formatted string to the CLI console
* Write formatted string to the static CLI interpreter output.
*
* Requires `OPENTHREAD_CONFIG_CLI_STATIC_INTERPRETER_ENABLE`.
*
* This is intended for use by user-provided CLI command handlers.
*
* @param[in] aFmt A pointer to the format string.
* @param[in] ... A matching list of arguments.
@@ -124,7 +205,11 @@ void otCliOutputBytes(const uint8_t *aBytes, uint8_t aLength);
void otCliOutputFormat(const char *aFmt, ...) OT_TOOL_PRINTF_STYLE_FORMAT_ARG_CHECK(1, 2);
/**
* Write error code to the CLI console
* Write a given error code as the result of previous command to the static CLI interpreter output.
*
* Requires `OPENTHREAD_CONFIG_CLI_STATIC_INTERPRETER_ENABLE`.
*
* This is intended for use by user-provided CLI command handlers.
*
* If the @p aError is `OT_ERROR_PENDING` nothing will be outputted.
*
@@ -133,7 +218,7 @@ void otCliOutputFormat(const char *aFmt, ...) OT_TOOL_PRINTF_STYLE_FORMAT_ARG_CH
void otCliAppendResult(otError aError);
/**
* Callback to write the OpenThread Log to the CLI console
* Callback to write the OpenThread Log to the static CLI interpreter output.
*
* @param[in] aLogLevel The log level.
* @param[in] aLogRegion The log region.
@@ -148,6 +233,8 @@ void otCliPlatLogv(otLogLevel aLogLevel, otLogRegion aLogRegion, const char *aFo
*
* Available when `OPENTHREAD_CONFIG_CLI_VENDOR_COMMANDS_ENABLE` is enabled and
* `OPENTHREAD_CONFIG_CLI_MAX_USER_CMD_ENTRIES` is greater than 1.
*
* Requires `OPENTHREAD_CONFIG_CLI_STATIC_INTERPRETER_ENABLE`.
*/
extern void otCliVendorSetUserCommands(void);
+1 -1
View File
@@ -52,7 +52,7 @@ extern "C" {
*
* @note This number versions both OpenThread platform and user APIs.
*/
#define OPENTHREAD_API_VERSION (595)
#define OPENTHREAD_API_VERSION (596)
/**
* @addtogroup api-instance
+42 -7
View File
@@ -71,9 +71,6 @@
namespace ot {
namespace Cli {
Interpreter *Interpreter::sInterpreter = nullptr;
static OT_DEFINE_ALIGNED_VAR(sInterpreterRaw, sizeof(Interpreter), uint64_t);
Interpreter::Interpreter(Instance *aInstance, otCliOutputCallback aCallback, void *aContext)
: OutputImplementer(aCallback, aContext)
, Utils(aInstance, *this)
@@ -8504,11 +8501,49 @@ void Interpreter::HandleWakeupResult(otError aError) { OutputResult(aError); }
#endif // OPENTHREAD_FTD || OPENTHREAD_MTD
void Interpreter::Initialize(otInstance *aInstance, otCliOutputCallback aCallback, void *aContext)
{
Instance *instance = static_cast<Instance *>(aInstance);
size_t Interpreter::GetSize(void) { return sizeof(Interpreter); }
Interpreter::sInterpreter = new (&sInterpreterRaw) Interpreter(instance, aCallback, aContext);
Interpreter *Interpreter::Init(void *aBuffer,
size_t aSize,
otInstance *aInstance,
otCliOutputCallback aCallback,
void *aContext)
{
Interpreter *interpreter = nullptr;
Instance *instance = static_cast<Instance *>(aInstance);
VerifyOrExit(aSize >= sizeof(Interpreter));
interpreter = new (aBuffer) Interpreter(instance, aCallback, aContext);
exit:
return interpreter;
}
#if OPENTHREAD_CONFIG_CLI_STATIC_INTERPRETER_ENABLE
Interpreter *Interpreter::sInterpreter = nullptr;
static OT_DEFINE_ALIGNED_VAR(sInterpreterRaw, sizeof(Interpreter), uint64_t);
void Interpreter::Init(otInstance *aInstance, otCliOutputCallback aCallback, void *aContext)
{
sInterpreter = Init(&sInterpreterRaw, sizeof(sInterpreterRaw), aInstance, aCallback, aContext);
}
#endif
void Interpreter::Finalize(void)
{
mTimer.Stop();
#if (OPENTHREAD_FTD || OPENTHREAD_MTD) && OPENTHREAD_CONFIG_CLI_REGISTER_IP6_RECV_CALLBACK
otIp6SetReceiveCallback(GetInstancePtr(), nullptr, nullptr);
#endif
#if OPENTHREAD_CONFIG_DIAG_ENABLE
otDiagSetOutputCallback(GetInstancePtr(), nullptr, nullptr);
#endif
this->~Interpreter();
}
void Interpreter::OutputPrompt(void)
+44 -7
View File
@@ -86,6 +86,10 @@
#include "common/type_traits.hpp"
#include "instance/instance.hpp"
typedef struct otCliInterpreter
{
} otCliInterpreter;
namespace ot {
/**
@@ -103,7 +107,7 @@ extern "C" void otCliOutputFormat(const char *aFmt, ...);
/**
* Implements the CLI interpreter.
*/
class Interpreter : public OutputImplementer, public Utils
class Interpreter : public otCliInterpreter, public OutputImplementer, public Utils
{
#if OPENTHREAD_FTD || OPENTHREAD_MTD
friend class Ba;
@@ -138,34 +142,65 @@ public:
*/
explicit Interpreter(Instance *aInstance, otCliOutputCallback aCallback, void *aContext);
#if OPENTHREAD_CONFIG_CLI_STATIC_INTERPRETER_ENABLE
/**
* Returns a reference to the interpreter object.
* Returns a reference to the static CLI interpreter.
*
* @returns A reference to the interpreter object.
* @returns A reference to the static CLI interpreter.
*/
static Interpreter &GetInterpreter(void)
{
OT_ASSERT(sInterpreter != nullptr);
return *sInterpreter;
}
/**
* Initializes the Console interpreter.
* Initializes the static CLI interpreter.
*
* @param[in] aInstance The OpenThread instance structure.
* @param[in] aCallback A pointer to a callback method.
* @param[in] aContext A pointer to a user context.
*/
static void Initialize(otInstance *aInstance, otCliOutputCallback aCallback, void *aContext);
static void Init(otInstance *aInstance, otCliOutputCallback aCallback, void *aContext);
/**
* Returns whether the interpreter is initialized.
* Returns whether the static CLI interpreter is initialized.
*
* @returns Whether the interpreter is initialized.
*/
static bool IsInitialized(void) { return sInterpreter != nullptr; }
#endif // OPENTHREAD_CONFIG_CLI_STATIC_INTERPRETER_ENABLE
/**
* Gets the size of the CLI interpreter object.
*
* @returns The size of the CLI interpreter object in bytes.
*/
static size_t GetSize(void);
/**
* Initializes a CLI interpreter.
*
* @param[in] aBuffer A pointer to a memory buffer for the CLI interpreter.
* @param[in] aSize The size of the memory buffer.
* @param[in] aInstance The OpenThread instance structure.
* @param[in] aCallback A callback method called to process CLI output.
* @param[in] aContext A user context pointer.
*
* @returns A pointer to the initialized CLI interpreter, or `nullptr` if @p aSize is too small.
*/
static Interpreter *Init(void *aBuffer,
size_t aSize,
otInstance *aInstance,
otCliOutputCallback aCallback,
void *aContext);
/**
* Finalizes the CLI interpreter.
*/
void Finalize(void);
/**
* Interprets a CLI command.
*
@@ -197,7 +232,9 @@ public:
#endif
protected:
#if OPENTHREAD_CONFIG_CLI_STATIC_INTERPRETER_ENABLE
static Interpreter *sInterpreter;
#endif
private:
static constexpr uint8_t kIndentSize = 4;
+38 -2
View File
@@ -38,16 +38,50 @@
namespace ot {
namespace Cli {
extern "C" size_t otCliInterpreterGetSize(void) { return Interpreter::GetSize(); }
extern "C" otCliInterpreter *otCliInterpreterInit(void *aBuffer,
size_t aSize,
otInstance *aInstance,
otCliOutputCallback aCallback,
void *aContext)
{
return Interpreter::Init(aBuffer, aSize, aInstance, aCallback, aContext);
}
#if OPENTHREAD_CONFIG_CLI_PROMPT_ENABLE
extern "C" void otCliInterpreterSetPromptConfig(otCliInterpreter *aInterpreter, bool aEnable)
{
static_cast<Interpreter *>(aInterpreter)->SetPromptConfig(aEnable);
}
#endif
extern "C" void otCliInterpreterInputLine(otCliInterpreter *aInterpreter, char *aLine)
{
static_cast<Interpreter *>(aInterpreter)->ProcessLine(aLine);
}
extern "C" void otCliInterpreterFinalize(otCliInterpreter *aInterpreter)
{
static_cast<Interpreter *>(aInterpreter)->Finalize();
}
//---------------------------------------------------------------------------------------------------------------------
#if OPENTHREAD_CONFIG_CLI_STATIC_INTERPRETER_ENABLE
extern "C" void otCliInit(otInstance *aInstance, otCliOutputCallback aCallback, void *aContext)
{
Interpreter::Initialize(aInstance, aCallback, aContext);
Interpreter::Init(aInstance, aCallback, aContext);
#if OPENTHREAD_CONFIG_CLI_VENDOR_COMMANDS_ENABLE && OPENTHREAD_CONFIG_CLI_MAX_USER_CMD_ENTRIES > 1
otCliVendorSetUserCommands();
#endif
}
extern "C" void otCliInputLine(char *aBuf) { Interpreter::GetInterpreter().ProcessLine(aBuf); }
extern "C" otCliInterpreter *otCliGetStaticInterpreter(void) { return &Interpreter::GetInterpreter(); }
extern "C" void otCliInputLine(char *aLine) { Interpreter::GetInterpreter().ProcessLine(aLine); }
extern "C" otError otCliSetUserCommands(const otCliCommand *aUserCommands, uint8_t aLength, void *aContext)
{
@@ -88,5 +122,7 @@ exit:
return;
}
#endif // OPENTHREAD_CONFIG_CLI_STATIC_INTERPRETER_ENABLE
} // namespace Cli
} // namespace ot
+15
View File
@@ -47,6 +47,21 @@
#endif
#endif
/**
* @def OPENTHREAD_CONFIG_CLI_STATIC_INTERPRETER_ENABLE
*
* Define as 1 to enable the static CLI interpreter.
*
* This configuration option enables the static CLI interpreter, allowing the CLI module to statically allocate and
* provide a single interpreter instance.
*
* This is intended to provide backward compatibility with the original `otCli*` APIs. It can be disabled to save RAM
* if the static CLI interpreter is not needed.
*/
#ifndef OPENTHREAD_CONFIG_CLI_STATIC_INTERPRETER_ENABLE
#define OPENTHREAD_CONFIG_CLI_STATIC_INTERPRETER_ENABLE 1
#endif
/**
* @def OPENTHREAD_CONFIG_CLI_MAX_LINE_LENGTH
*