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