1 /*
2 * Copyright (c) 2024 Basalte bv
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/shell/shell_rpmsg.h>
8
9 SHELL_RPMSG_DEFINE(shell_transport_rpmsg);
10 SHELL_DEFINE(shell_rpmsg, CONFIG_SHELL_PROMPT_RPMSG, &shell_transport_rpmsg,
11 CONFIG_SHELL_BACKEND_RPMSG_LOG_MESSAGE_QUEUE_SIZE,
12 CONFIG_SHELL_BACKEND_RPMSG_LOG_MESSAGE_QUEUE_TIMEOUT, SHELL_FLAG_OLF_CRLF);
13
rpmsg_shell_cb(struct rpmsg_endpoint * ept,void * data,size_t len,uint32_t src,void * priv)14 static int rpmsg_shell_cb(struct rpmsg_endpoint *ept, void *data,
15 size_t len, uint32_t src, void *priv)
16 {
17 const struct shell_transport *transport = (const struct shell_transport *)priv;
18 struct shell_rpmsg *sh_rpmsg = (struct shell_rpmsg *)transport->ctx;
19 struct shell_rpmsg_rx rx;
20
21 if (len == 0) {
22 return RPMSG_ERR_NO_BUFF;
23 }
24
25 rx.data = data;
26 rx.len = len;
27 if (k_msgq_put(&sh_rpmsg->rx_q, &rx, K_NO_WAIT) != 0) {
28 return RPMSG_ERR_NO_MEM;
29 }
30
31 rpmsg_hold_rx_buffer(ept, data);
32 sh_rpmsg->shell_handler(SHELL_TRANSPORT_EVT_RX_RDY, sh_rpmsg->shell_context);
33
34 return RPMSG_SUCCESS;
35 }
36
uninit(const struct shell_transport * transport)37 static int uninit(const struct shell_transport *transport)
38 {
39 struct shell_rpmsg *sh_rpmsg = (struct shell_rpmsg *)transport->ctx;
40
41 if (!sh_rpmsg->ready) {
42 return -ENODEV;
43 }
44
45 rpmsg_destroy_ept(&sh_rpmsg->ept);
46 sh_rpmsg->ready = false;
47
48 return 0;
49 }
50
init(const struct shell_transport * transport,const void * config,shell_transport_handler_t evt_handler,void * context)51 static int init(const struct shell_transport *transport,
52 const void *config,
53 shell_transport_handler_t evt_handler,
54 void *context)
55 {
56 struct shell_rpmsg *sh_rpmsg = (struct shell_rpmsg *)transport->ctx;
57 struct rpmsg_device *rdev;
58 int ret;
59
60 if (sh_rpmsg->ready) {
61 return -EALREADY;
62 }
63
64 if (config == NULL) {
65 return -EINVAL;
66 }
67 rdev = (struct rpmsg_device *)config;
68
69 k_msgq_init(&sh_rpmsg->rx_q, (char *)sh_rpmsg->rx_buf, sizeof(struct shell_rpmsg_rx),
70 CONFIG_SHELL_RPMSG_MAX_RX);
71
72 ret = rpmsg_create_ept(&sh_rpmsg->ept, rdev, CONFIG_SHELL_RPMSG_SERVICE_NAME,
73 CONFIG_SHELL_RPMSG_SRC_ADDR, CONFIG_SHELL_RPMSG_DST_ADDR,
74 rpmsg_shell_cb, NULL);
75 if (ret < 0) {
76 return ret;
77 }
78
79 sh_rpmsg->ept.priv = (void *)transport;
80
81 sh_rpmsg->shell_handler = evt_handler;
82 sh_rpmsg->shell_context = context;
83 sh_rpmsg->ready = true;
84
85 return 0;
86 }
87
enable(const struct shell_transport * transport,bool blocking)88 static int enable(const struct shell_transport *transport, bool blocking)
89 {
90 struct shell_rpmsg *sh_rpmsg = (struct shell_rpmsg *)transport->ctx;
91
92 if (!sh_rpmsg->ready) {
93 return -ENODEV;
94 }
95
96 sh_rpmsg->blocking = blocking;
97
98 return 0;
99 }
100
write(const struct shell_transport * transport,const void * data,size_t length,size_t * cnt)101 static int write(const struct shell_transport *transport,
102 const void *data, size_t length, size_t *cnt)
103 {
104 struct shell_rpmsg *sh_rpmsg = (struct shell_rpmsg *)transport->ctx;
105 int ret;
106
107 *cnt = 0;
108
109 if (!sh_rpmsg->ready) {
110 return -ENODEV;
111 }
112
113 if (sh_rpmsg->blocking) {
114 ret = rpmsg_send(&sh_rpmsg->ept, data, (int)length);
115 } else {
116 ret = rpmsg_trysend(&sh_rpmsg->ept, data, (int)length);
117 }
118
119 /* Set TX ready in any case, as we have no way to recover otherwise */
120 sh_rpmsg->shell_handler(SHELL_TRANSPORT_EVT_TX_RDY, sh_rpmsg->shell_context);
121
122 if (ret < 0) {
123 return ret;
124 }
125
126 *cnt = (size_t)ret;
127
128 return 0;
129 }
130
read(const struct shell_transport * transport,void * data,size_t length,size_t * cnt)131 static int read(const struct shell_transport *transport,
132 void *data, size_t length, size_t *cnt)
133 {
134 struct shell_rpmsg *sh_rpmsg = (struct shell_rpmsg *)transport->ctx;
135 struct shell_rpmsg_rx *rx = &sh_rpmsg->rx_cur;
136 size_t read_len;
137 bool release = true;
138
139 if (!sh_rpmsg->ready) {
140 return -ENODEV;
141 }
142
143 /* Check if we still have pending data */
144 if (rx->data == NULL) {
145 int ret = k_msgq_get(&sh_rpmsg->rx_q, rx, K_NO_WAIT);
146
147 if (ret < 0) {
148 rx->data = NULL;
149 goto no_data;
150 }
151
152 __ASSERT_NO_MSG(rx->len > 0);
153 sh_rpmsg->rx_consumed = 0;
154 }
155
156 __ASSERT_NO_MSG(rx->len > sh_rpmsg->rx_consumed);
157 read_len = rx->len - sh_rpmsg->rx_consumed;
158 if (read_len > length) {
159 read_len = length;
160 release = false;
161 }
162
163 *cnt = read_len;
164 memcpy(data, &((char *)rx->data)[sh_rpmsg->rx_consumed], read_len);
165
166 if (release) {
167 rpmsg_release_rx_buffer(&sh_rpmsg->ept, rx->data);
168 rx->data = NULL;
169 } else {
170 sh_rpmsg->rx_consumed += read_len;
171 }
172
173 return 0;
174
175 no_data:
176 *cnt = 0;
177 return 0;
178 }
179
180 const struct shell_transport_api shell_rpmsg_transport_api = {
181 .init = init,
182 .uninit = uninit,
183 .enable = enable,
184 .read = read,
185 .write = write,
186 };
187
shell_backend_rpmsg_init_transport(struct rpmsg_device * rpmsg_dev)188 int shell_backend_rpmsg_init_transport(struct rpmsg_device *rpmsg_dev)
189 {
190 bool log_backend = CONFIG_SHELL_RPMSG_INIT_LOG_LEVEL > 0;
191 uint32_t level = (CONFIG_SHELL_RPMSG_INIT_LOG_LEVEL > LOG_LEVEL_DBG) ?
192 CONFIG_LOG_MAX_LEVEL : CONFIG_SHELL_RPMSG_INIT_LOG_LEVEL;
193 static const struct shell_backend_config_flags cfg_flags =
194 SHELL_DEFAULT_BACKEND_CONFIG_FLAGS;
195
196 return shell_init(&shell_rpmsg, rpmsg_dev, cfg_flags, log_backend, level);
197 }
198
shell_backend_rpmsg_get_ptr(void)199 const struct shell *shell_backend_rpmsg_get_ptr(void)
200 {
201 return &shell_rpmsg;
202 }
203