/* * Copyright (c) 2024 Basalte bv * * SPDX-License-Identifier: Apache-2.0 */ #include 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; }