shell: backends: Add RPMsg shell backend
Introduce a backend for the Shell subsystem using a RPMsg endpoint. This is useful for tooling & testing a remote processor from linux. Signed-off-by: Pieter De Gendt <pieter.degendt@basalte.be>
This commit is contained in:
parent
afa65e4bbe
commit
df7750f63a
4 changed files with 379 additions and 0 deletions
87
include/zephyr/shell/shell_rpmsg.h
Normal file
87
include/zephyr/shell/shell_rpmsg.h
Normal file
|
@ -0,0 +1,87 @@
|
|||
/*
|
||||
* Copyright (c) 2024 Basalte bv
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef SHELL_RPMSG_H__
|
||||
#define SHELL_RPMSG_H__
|
||||
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/shell/shell.h>
|
||||
#include <openamp/rpmsg.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern const struct shell_transport_api shell_rpmsg_transport_api;
|
||||
|
||||
/** RPMsg received message placeholder */
|
||||
struct shell_rpmsg_rx {
|
||||
/** Pointer to the data held by RPMsg endpoint */
|
||||
void *data;
|
||||
/** The length of the data */
|
||||
size_t len;
|
||||
};
|
||||
|
||||
/** RPMsg-based shell transport. */
|
||||
struct shell_rpmsg {
|
||||
/** Handler function registered by shell. */
|
||||
shell_transport_handler_t shell_handler;
|
||||
|
||||
/** Context registered by shell. */
|
||||
void *shell_context;
|
||||
|
||||
/** Indicator if we are ready to read/write */
|
||||
bool ready;
|
||||
|
||||
/** Setting for blocking mode */
|
||||
bool blocking;
|
||||
|
||||
/** RPMsg endpoint */
|
||||
struct rpmsg_endpoint ept;
|
||||
|
||||
/** Queue for received data. */
|
||||
struct k_msgq rx_q;
|
||||
|
||||
/** Buffer for received messages */
|
||||
struct shell_rpmsg_rx rx_buf[CONFIG_SHELL_RPMSG_MAX_RX];
|
||||
|
||||
/** The current rx message */
|
||||
struct shell_rpmsg_rx rx_cur;
|
||||
|
||||
/** The number of bytes consumed from rx_cur */
|
||||
size_t rx_consumed;
|
||||
};
|
||||
|
||||
#define SHELL_RPMSG_DEFINE(_name) \
|
||||
static struct shell_rpmsg _name##_shell_rpmsg; \
|
||||
struct shell_transport _name = { \
|
||||
.api = &shell_rpmsg_transport_api, \
|
||||
.ctx = (struct shell_rpmsg *)&_name##_shell_rpmsg, \
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initialize the Shell backend using the provided @p rpmsg_dev device.
|
||||
*
|
||||
* @param rpmsg_dev A pointer to an RPMsg device
|
||||
* @return 0 on success or a negative value on error
|
||||
*/
|
||||
int shell_backend_rpmsg_init_transport(struct rpmsg_device *rpmsg_dev);
|
||||
|
||||
/**
|
||||
* @brief This function provides pointer to shell RPMsg backend instance.
|
||||
*
|
||||
* Function returns pointer to the shell RPMsg instance. This instance can be
|
||||
* next used with shell_execute_cmd function in order to test commands behavior.
|
||||
*
|
||||
* @returns Pointer to the shell instance.
|
||||
*/
|
||||
const struct shell *shell_backend_rpmsg_get_ptr(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* SHELL_RPMSG_H__ */
|
|
@ -24,3 +24,8 @@ zephyr_sources_ifdef(
|
|||
CONFIG_SHELL_BACKEND_MQTT
|
||||
shell_mqtt.c
|
||||
)
|
||||
|
||||
zephyr_sources_ifdef(
|
||||
CONFIG_SHELL_BACKEND_RPMSG
|
||||
shell_rpmsg.c
|
||||
)
|
||||
|
|
|
@ -354,6 +354,91 @@ source "subsys/logging/Kconfig.template.log_config"
|
|||
|
||||
endif # SHELL_BACKEND_MQTT
|
||||
|
||||
config SHELL_BACKEND_RPMSG
|
||||
bool "RPMsg backend."
|
||||
depends on OPENAMP
|
||||
help
|
||||
Enable RPMsg backend.
|
||||
|
||||
if SHELL_BACKEND_RPMSG
|
||||
|
||||
config SHELL_PROMPT_RPMSG
|
||||
string "Displayed prompt name"
|
||||
default "ipc:~$ "
|
||||
help
|
||||
Displayed prompt name for RPMsg backend. If prompt is set, the shell will
|
||||
send two newlines during initialization.
|
||||
|
||||
config SHELL_RPMSG_SERVICE_NAME
|
||||
string "Service name"
|
||||
default "rpmsg-tty"
|
||||
help
|
||||
The service name associated with the RPMsg endpoint.
|
||||
|
||||
config SHELL_RPMSG_SRC_ADDR
|
||||
hex "Local address"
|
||||
default 0xffffffff # The ANY address
|
||||
help
|
||||
Local address of the RPMsg endpoint.
|
||||
|
||||
config SHELL_RPMSG_DST_ADDR
|
||||
hex "Remote address"
|
||||
default 0xffffffff # The ANY address
|
||||
help
|
||||
Target address of the RPMsg endpoint.
|
||||
|
||||
config SHELL_RPMSG_MAX_RX
|
||||
int "Receive buffer size"
|
||||
default 10
|
||||
help
|
||||
The maximum number of received messages to be queued.
|
||||
|
||||
module = SHELL_BACKEND_RPMSG
|
||||
default-timeout = 100
|
||||
source "subsys/shell/Kconfig.template.shell_log_queue_timeout"
|
||||
|
||||
default-size = 512
|
||||
source "subsys/shell/Kconfig.template.shell_log_queue_size"
|
||||
|
||||
choice
|
||||
prompt "Initial log level limit"
|
||||
default SHELL_RPMSG_INIT_LOG_LEVEL_DEFAULT
|
||||
|
||||
config SHELL_RPMSG_INIT_LOG_LEVEL_DEFAULT
|
||||
bool "System limit (LOG_MAX_LEVEL)"
|
||||
|
||||
config SHELL_RPMSG_INIT_LOG_LEVEL_DBG
|
||||
bool "Debug"
|
||||
|
||||
config SHELL_RPMSG_INIT_LOG_LEVEL_INF
|
||||
bool "Info"
|
||||
|
||||
config SHELL_RPMSG_INIT_LOG_LEVEL_WRN
|
||||
bool "Warning"
|
||||
|
||||
config SHELL_RPMSG_INIT_LOG_LEVEL_ERR
|
||||
bool "Error"
|
||||
|
||||
config SHELL_RPMSG_INIT_LOG_LEVEL_NONE
|
||||
bool "None"
|
||||
|
||||
endchoice # SHELL_RPMSG_INIT_LOG_LEVEL
|
||||
|
||||
config SHELL_RPMSG_INIT_LOG_LEVEL
|
||||
int
|
||||
default 0 if SHELL_RPMSG_INIT_LOG_LEVEL_NONE
|
||||
default 1 if SHELL_RPMSG_INIT_LOG_LEVEL_ERR
|
||||
default 2 if SHELL_RPMSG_INIT_LOG_LEVEL_WRN
|
||||
default 3 if SHELL_RPMSG_INIT_LOG_LEVEL_INF
|
||||
default 4 if SHELL_RPMSG_INIT_LOG_LEVEL_DBG
|
||||
default 5 if SHELL_RPMSG_INIT_LOG_LEVEL_DEFAULT
|
||||
|
||||
module = SHELL_RPMSG
|
||||
module-str = RPMsg shell backend
|
||||
source "subsys/logging/Kconfig.template.log_config"
|
||||
|
||||
endif # SHELL_BACKEND_RPMSG
|
||||
|
||||
config SHELL_BACKEND_TELNET
|
||||
bool "TELNET backend."
|
||||
depends on NET_TCP
|
||||
|
|
202
subsys/shell/backends/shell_rpmsg.c
Normal file
202
subsys/shell/backends/shell_rpmsg.c
Normal file
|
@ -0,0 +1,202 @@
|
|||
/*
|
||||
* Copyright (c) 2024 Basalte bv
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr/shell/shell_rpmsg.h>
|
||||
|
||||
SHELL_RPMSG_DEFINE(shell_transport_rpmsg);
|
||||
SHELL_DEFINE(shell_rpmsg, CONFIG_SHELL_PROMPT_RPMSG, &shell_transport_rpmsg,
|
||||
CONFIG_SHELL_BACKEND_RPMSG_LOG_MESSAGE_QUEUE_SIZE,
|
||||
CONFIG_SHELL_BACKEND_RPMSG_LOG_MESSAGE_QUEUE_TIMEOUT, SHELL_FLAG_OLF_CRLF);
|
||||
|
||||
static int rpmsg_shell_cb(struct rpmsg_endpoint *ept, void *data,
|
||||
size_t len, uint32_t src, void *priv)
|
||||
{
|
||||
const struct shell_transport *transport = (const struct shell_transport *)priv;
|
||||
struct shell_rpmsg *sh_rpmsg = (struct shell_rpmsg *)transport->ctx;
|
||||
struct shell_rpmsg_rx rx;
|
||||
|
||||
if (len == 0) {
|
||||
return RPMSG_ERR_NO_BUFF;
|
||||
}
|
||||
|
||||
rx.data = data;
|
||||
rx.len = len;
|
||||
if (k_msgq_put(&sh_rpmsg->rx_q, &rx, K_NO_WAIT) != 0) {
|
||||
return RPMSG_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
rpmsg_hold_rx_buffer(ept, data);
|
||||
sh_rpmsg->shell_handler(SHELL_TRANSPORT_EVT_RX_RDY, sh_rpmsg->shell_context);
|
||||
|
||||
return RPMSG_SUCCESS;
|
||||
}
|
||||
|
||||
static int uninit(const struct shell_transport *transport)
|
||||
{
|
||||
struct shell_rpmsg *sh_rpmsg = (struct shell_rpmsg *)transport->ctx;
|
||||
|
||||
if (!sh_rpmsg->ready) {
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
rpmsg_destroy_ept(&sh_rpmsg->ept);
|
||||
sh_rpmsg->ready = false;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int init(const struct shell_transport *transport,
|
||||
const void *config,
|
||||
shell_transport_handler_t evt_handler,
|
||||
void *context)
|
||||
{
|
||||
struct shell_rpmsg *sh_rpmsg = (struct shell_rpmsg *)transport->ctx;
|
||||
struct rpmsg_device *rdev;
|
||||
int ret;
|
||||
|
||||
if (sh_rpmsg->ready) {
|
||||
return -EALREADY;
|
||||
}
|
||||
|
||||
if (config == NULL) {
|
||||
return -EINVAL;
|
||||
}
|
||||
rdev = (struct rpmsg_device *)config;
|
||||
|
||||
k_msgq_init(&sh_rpmsg->rx_q, (char *)sh_rpmsg->rx_buf, sizeof(struct shell_rpmsg_rx),
|
||||
CONFIG_SHELL_RPMSG_MAX_RX);
|
||||
|
||||
ret = rpmsg_create_ept(&sh_rpmsg->ept, rdev, CONFIG_SHELL_RPMSG_SERVICE_NAME,
|
||||
CONFIG_SHELL_RPMSG_SRC_ADDR, CONFIG_SHELL_RPMSG_DST_ADDR,
|
||||
rpmsg_shell_cb, NULL);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
sh_rpmsg->ept.priv = (void *)transport;
|
||||
|
||||
sh_rpmsg->shell_handler = evt_handler;
|
||||
sh_rpmsg->shell_context = context;
|
||||
sh_rpmsg->ready = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int enable(const struct shell_transport *transport, bool blocking)
|
||||
{
|
||||
struct shell_rpmsg *sh_rpmsg = (struct shell_rpmsg *)transport->ctx;
|
||||
|
||||
if (!sh_rpmsg->ready) {
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
sh_rpmsg->blocking = blocking;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int write(const struct shell_transport *transport,
|
||||
const void *data, size_t length, size_t *cnt)
|
||||
{
|
||||
struct shell_rpmsg *sh_rpmsg = (struct shell_rpmsg *)transport->ctx;
|
||||
int ret;
|
||||
|
||||
*cnt = 0;
|
||||
|
||||
if (!sh_rpmsg->ready) {
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (sh_rpmsg->blocking) {
|
||||
ret = rpmsg_send(&sh_rpmsg->ept, data, (int)length);
|
||||
} else {
|
||||
ret = rpmsg_trysend(&sh_rpmsg->ept, data, (int)length);
|
||||
}
|
||||
|
||||
/* Set TX ready in any case, as we have no way to recover otherwise */
|
||||
sh_rpmsg->shell_handler(SHELL_TRANSPORT_EVT_TX_RDY, sh_rpmsg->shell_context);
|
||||
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
*cnt = (size_t)ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int read(const struct shell_transport *transport,
|
||||
void *data, size_t length, size_t *cnt)
|
||||
{
|
||||
struct shell_rpmsg *sh_rpmsg = (struct shell_rpmsg *)transport->ctx;
|
||||
struct shell_rpmsg_rx *rx = &sh_rpmsg->rx_cur;
|
||||
size_t read_len;
|
||||
bool release = true;
|
||||
|
||||
if (!sh_rpmsg->ready) {
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* Check if we still have pending data */
|
||||
if (rx->data == NULL) {
|
||||
int ret = k_msgq_get(&sh_rpmsg->rx_q, rx, K_NO_WAIT);
|
||||
|
||||
if (ret < 0) {
|
||||
rx->data = NULL;
|
||||
goto no_data;
|
||||
}
|
||||
|
||||
__ASSERT_NO_MSG(rx->len > 0);
|
||||
sh_rpmsg->rx_consumed = 0;
|
||||
}
|
||||
|
||||
__ASSERT_NO_MSG(rx->len > sh_rpmsg->rx_consumed);
|
||||
read_len = rx->len - sh_rpmsg->rx_consumed;
|
||||
if (read_len > length) {
|
||||
read_len = length;
|
||||
release = false;
|
||||
}
|
||||
|
||||
*cnt = read_len;
|
||||
memcpy(data, &((char *)rx->data)[sh_rpmsg->rx_consumed], read_len);
|
||||
|
||||
if (release) {
|
||||
rpmsg_release_rx_buffer(&sh_rpmsg->ept, rx->data);
|
||||
rx->data = NULL;
|
||||
} else {
|
||||
sh_rpmsg->rx_consumed += read_len;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
no_data:
|
||||
*cnt = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct shell_transport_api shell_rpmsg_transport_api = {
|
||||
.init = init,
|
||||
.uninit = uninit,
|
||||
.enable = enable,
|
||||
.read = read,
|
||||
.write = write,
|
||||
};
|
||||
|
||||
int shell_backend_rpmsg_init_transport(struct rpmsg_device *rpmsg_dev)
|
||||
{
|
||||
bool log_backend = CONFIG_SHELL_RPMSG_INIT_LOG_LEVEL > 0;
|
||||
uint32_t level = (CONFIG_SHELL_RPMSG_INIT_LOG_LEVEL > LOG_LEVEL_DBG) ?
|
||||
CONFIG_LOG_MAX_LEVEL : CONFIG_SHELL_RPMSG_INIT_LOG_LEVEL;
|
||||
static const struct shell_backend_config_flags cfg_flags =
|
||||
SHELL_DEFAULT_BACKEND_CONFIG_FLAGS;
|
||||
|
||||
return shell_init(&shell_rpmsg, rpmsg_dev, cfg_flags, log_backend, level);
|
||||
}
|
||||
|
||||
const struct shell *shell_backend_rpmsg_get_ptr(void)
|
||||
{
|
||||
return &shell_rpmsg;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue