1 /*
2  * Copyright Runtime.io 2018. All rights reserved.
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr.h>
8 #include "net/buf.h"
9 #include "mgmt/mgmt.h"
10 #include "mgmt/mcumgr/buf.h"
11 #include "smp/smp.h"
12 #include "mgmt/mcumgr/smp.h"
13 
14 static mgmt_alloc_rsp_fn zephyr_smp_alloc_rsp;
15 static mgmt_trim_front_fn zephyr_smp_trim_front;
16 static mgmt_reset_buf_fn zephyr_smp_reset_buf;
17 static mgmt_write_at_fn zephyr_smp_write_at;
18 static mgmt_init_reader_fn zephyr_smp_init_reader;
19 static mgmt_init_writer_fn zephyr_smp_init_writer;
20 static mgmt_free_buf_fn zephyr_smp_free_buf;
21 static smp_tx_rsp_fn zephyr_smp_tx_rsp;
22 
23 static const struct mgmt_streamer_cfg zephyr_smp_cbor_cfg = {
24 	.alloc_rsp = zephyr_smp_alloc_rsp,
25 	.trim_front = zephyr_smp_trim_front,
26 	.reset_buf = zephyr_smp_reset_buf,
27 	.write_at = zephyr_smp_write_at,
28 	.init_reader = zephyr_smp_init_reader,
29 	.init_writer = zephyr_smp_init_writer,
30 	.free_buf = zephyr_smp_free_buf,
31 };
32 
33 void *
zephyr_smp_alloc_rsp(const void * req,void * arg)34 zephyr_smp_alloc_rsp(const void *req, void *arg)
35 {
36 	const struct net_buf_pool *pool;
37 	const struct net_buf *req_nb;
38 	struct net_buf *rsp_nb;
39 	struct zephyr_smp_transport *zst = arg;
40 
41 	req_nb = req;
42 
43 	rsp_nb = mcumgr_buf_alloc();
44 	if (rsp_nb == NULL) {
45 		return NULL;
46 	}
47 
48 	if (zst->zst_ud_copy) {
49 		zst->zst_ud_copy(rsp_nb, req_nb);
50 	} else {
51 		pool = net_buf_pool_get(req_nb->pool_id);
52 		memcpy(net_buf_user_data(rsp_nb),
53 		       net_buf_user_data((void *)req_nb),
54 		       sizeof(req_nb->user_data));
55 	}
56 
57 	return rsp_nb;
58 }
59 
60 static void
zephyr_smp_trim_front(void * buf,size_t len,void * arg)61 zephyr_smp_trim_front(void *buf, size_t len, void *arg)
62 {
63 	struct net_buf *nb;
64 
65 	nb = buf;
66 	if (len > nb->len) {
67 		len = nb->len;
68 	}
69 
70 	net_buf_pull(nb, len);
71 }
72 
73 /**
74  * Splits an appropriately-sized fragment from the front of a net_buf, as
75  * neeeded.  If the length of the net_buf is greater than specified maximum
76  * fragment size, a new net_buf is allocated, and data is moved from the source
77  * net_buf to the new net_buf.  If the net_buf is small enough to fit in a
78  * single fragment, the source net_buf is returned unmodified, and the supplied
79  * pointer is set to NULL.
80  *
81  * This function is expected to be called in a loop until the entire source
82  * net_buf has been consumed.  For example:
83  *
84  *     struct net_buf *frag;
85  *     struct net_buf *rsp;
86  *     ...
87  *     while (rsp != NULL) {
88  *         frag = zephyr_smp_split_frag(&rsp, zst, get_mtu());
89  *         if (frag == NULL) {
90  *             net_buf_unref(nb);
91  *             return SYS_ENOMEM;
92  *         }
93  *         send_packet(frag)
94  *     }
95  *
96  * @param nb                    The packet to fragment.  Upon fragmentation,
97  *                                  this net_buf is adjusted such that the
98  *                                  fragment data is removed.  If the packet
99  *                                  constitutes a single fragment, this gets
100  *                                  set to NULL on success.
101  * @param arg                   The zephyr SMP transport pointer.
102  * @param mtu                   The maximum payload size of a fragment.
103  *
104  * @return                      The next fragment to send on success;
105  *                              NULL on failure.
106  */
107 static struct net_buf *
zephyr_smp_split_frag(struct net_buf ** nb,void * arg,uint16_t mtu)108 zephyr_smp_split_frag(struct net_buf **nb, void *arg, uint16_t mtu)
109 {
110 	struct net_buf *frag;
111 	struct net_buf *src;
112 
113 	src = *nb;
114 
115 	if (src->len <= mtu) {
116 		*nb = NULL;
117 		frag = src;
118 	} else {
119 		frag = zephyr_smp_alloc_rsp(src, arg);
120 		if (!frag) {
121 			return NULL;
122 		}
123 
124 		/* Copy fragment payload into new buffer. */
125 		net_buf_add_mem(frag, src->data, mtu);
126 
127 		/* Remove fragment from total response. */
128 		zephyr_smp_trim_front(src, mtu, NULL);
129 	}
130 
131 	return frag;
132 }
133 
134 static void
zephyr_smp_reset_buf(void * buf,void * arg)135 zephyr_smp_reset_buf(void *buf, void *arg)
136 {
137 	net_buf_reset(buf);
138 }
139 
140 static int
zephyr_smp_write_at(struct cbor_encoder_writer * writer,size_t offset,const void * data,size_t len,void * arg)141 zephyr_smp_write_at(struct cbor_encoder_writer *writer, size_t offset,
142 		    const void *data, size_t len, void *arg)
143 {
144 	struct cbor_nb_writer *czw;
145 	struct net_buf *nb;
146 
147 	czw = (struct cbor_nb_writer *)writer;
148 	nb = czw->nb;
149 
150 	if (offset > nb->len) {
151 		return MGMT_ERR_EINVAL;
152 	}
153 
154 	if ((offset + len) > (nb->size - net_buf_headroom(nb))) {
155 		return MGMT_ERR_EINVAL;
156 	}
157 
158 	memcpy(nb->data + offset, data, len);
159 	if (nb->len < offset + len) {
160 		nb->len = offset + len;
161 		writer->bytes_written = nb->len;
162 	}
163 
164 	return 0;
165 }
166 
167 static int
zephyr_smp_tx_rsp(struct smp_streamer * ns,void * rsp,void * arg)168 zephyr_smp_tx_rsp(struct smp_streamer *ns, void *rsp, void *arg)
169 {
170 	struct zephyr_smp_transport *zst;
171 	struct net_buf *frag;
172 	struct net_buf *nb;
173 	uint16_t mtu;
174 	int rc;
175 	int i;
176 
177 	zst = arg;
178 	nb = rsp;
179 
180 	mtu = zst->zst_get_mtu(rsp);
181 	if (mtu == 0U) {
182 		/* The transport cannot support a transmission right now. */
183 		return MGMT_ERR_EUNKNOWN;
184 	}
185 
186 	i = 0;
187 	while (nb != NULL) {
188 		frag = zephyr_smp_split_frag(&nb, zst, mtu);
189 		if (frag == NULL) {
190 			zephyr_smp_free_buf(nb, zst);
191 			return MGMT_ERR_ENOMEM;
192 		}
193 
194 		rc = zst->zst_output(zst, frag);
195 		if (rc != 0) {
196 			return MGMT_ERR_EUNKNOWN;
197 		}
198 	}
199 
200 	return 0;
201 }
202 
203 static void
zephyr_smp_free_buf(void * buf,void * arg)204 zephyr_smp_free_buf(void *buf, void *arg)
205 {
206 	struct zephyr_smp_transport *zst = arg;
207 
208 	if (!buf) {
209 		return;
210 	}
211 
212 	if (zst->zst_ud_free) {
213 		zst->zst_ud_free(net_buf_user_data((struct net_buf *)buf));
214 	}
215 
216 	mcumgr_buf_free(buf);
217 }
218 
219 static int
zephyr_smp_init_reader(struct cbor_decoder_reader * reader,void * buf,void * arg)220 zephyr_smp_init_reader(struct cbor_decoder_reader *reader, void *buf,
221 		       void *arg)
222 {
223 	struct cbor_nb_reader *czr;
224 
225 	czr = (struct cbor_nb_reader *)reader;
226 	cbor_nb_reader_init(czr, buf);
227 
228 	return 0;
229 }
230 
231 static int
zephyr_smp_init_writer(struct cbor_encoder_writer * writer,void * buf,void * arg)232 zephyr_smp_init_writer(struct cbor_encoder_writer *writer, void *buf,
233 		       void *arg)
234 {
235 	struct cbor_nb_writer *czw;
236 
237 	czw = (struct cbor_nb_writer *)writer;
238 	cbor_nb_writer_init(czw, buf);
239 
240 	return 0;
241 }
242 
243 /**
244  * Processes a single SMP packet and sends the corresponding response(s).
245  */
246 static int
zephyr_smp_process_packet(struct zephyr_smp_transport * zst,struct net_buf * nb)247 zephyr_smp_process_packet(struct zephyr_smp_transport *zst,
248 			  struct net_buf *nb)
249 {
250 	struct cbor_nb_reader reader;
251 	struct cbor_nb_writer writer;
252 	struct smp_streamer streamer;
253 	int rc;
254 
255 	streamer = (struct smp_streamer) {
256 		.mgmt_stmr = {
257 			.cfg = &zephyr_smp_cbor_cfg,
258 			.reader = &reader.r,
259 			.writer = &writer.enc,
260 			.cb_arg = zst,
261 		},
262 		.tx_rsp_cb = zephyr_smp_tx_rsp,
263 	};
264 
265 	rc = smp_process_request_packet(&streamer, nb);
266 	return rc;
267 }
268 
269 /**
270  * Processes all received SNP request packets.
271  */
272 static void
zephyr_smp_handle_reqs(struct k_work * work)273 zephyr_smp_handle_reqs(struct k_work *work)
274 {
275 	struct zephyr_smp_transport *zst;
276 	struct net_buf *nb;
277 
278 	zst = (void *)work;
279 
280 	while ((nb = net_buf_get(&zst->zst_fifo, K_NO_WAIT)) != NULL) {
281 		zephyr_smp_process_packet(zst, nb);
282 	}
283 }
284 
285 void
zephyr_smp_transport_init(struct zephyr_smp_transport * zst,zephyr_smp_transport_out_fn * output_func,zephyr_smp_transport_get_mtu_fn * get_mtu_func,zephyr_smp_transport_ud_copy_fn * ud_copy_func,zephyr_smp_transport_ud_free_fn * ud_free_func)286 zephyr_smp_transport_init(struct zephyr_smp_transport *zst,
287 			  zephyr_smp_transport_out_fn *output_func,
288 			  zephyr_smp_transport_get_mtu_fn *get_mtu_func,
289 			  zephyr_smp_transport_ud_copy_fn *ud_copy_func,
290 			  zephyr_smp_transport_ud_free_fn *ud_free_func)
291 {
292 	*zst = (struct zephyr_smp_transport) {
293 		.zst_output = output_func,
294 		.zst_get_mtu = get_mtu_func,
295 		.zst_ud_copy = ud_copy_func,
296 		.zst_ud_free = ud_free_func,
297 	};
298 
299 	k_work_init(&zst->zst_work, zephyr_smp_handle_reqs);
300 	k_fifo_init(&zst->zst_fifo);
301 }
302 
303 void
zephyr_smp_rx_req(struct zephyr_smp_transport * zst,struct net_buf * nb)304 zephyr_smp_rx_req(struct zephyr_smp_transport *zst, struct net_buf *nb)
305 {
306 	net_buf_put(&zst->zst_fifo, nb);
307 	k_work_submit(&zst->zst_work);
308 }
309