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