1 /* SPDX-License-Identifier: GPL-2.0 */
2 /*
3 * Driver O/S-independent utility routines
4 *
5 * Copyright (C) 1999-2019, Broadcom.
6 *
7 * Unless you and Broadcom execute a separate written software license
8 * agreement governing use of this software, this software is licensed to you
9 * under the terms of the GNU General Public License version 2 (the "GPL"),
10 * available at http://www.broadcom.com/licenses/GPLv2.php, with the
11 * following added to such license:
12 *
13 * As a special exception, the copyright holders of this software give you
14 * permission to link this software with independent modules, and to copy and
15 * distribute the resulting executable under terms of your choice, provided that
16 * you also meet, for each linked independent module, the terms and conditions of
17 * the license of that module. An independent module is a module which is not
18 * derived from this software. The special exception does not apply to any
19 * modifications of the software.
20 *
21 * Notwithstanding the above, under no circumstances may you combine this
22 * software in any way with any other Broadcom software provided under a license
23 * other than the GPL, without Broadcom's express prior written consent.
24 *
25 *
26 * <<Broadcom-WL-IPTag/Open:>>
27 *
28 * $Id: bcmxtlv.c 788740 2018-11-13 21:45:01Z $
29 */
30
31 #include <bcm_cfg.h>
32
33 #include <typedefs.h>
34 #include <bcmdefs.h>
35
36 #include <stdarg.h>
37
38 #ifdef BCMDRIVER
39 #include <osl.h>
40 #else /* !BCMDRIVER */
41 #include <stdio.h>
42 #include <string.h>
43 #include <stdlib.h>
44 #ifndef ASSERT
45 #define ASSERT(exp)
46 #endif // endif
47 #endif /* !BCMDRIVER */
48
49 #include <bcmtlv.h>
50 #include <bcmendian.h>
51 #include <bcmutils.h>
52
53 int
bcm_xtlv_hdr_size(bcm_xtlv_opts_t opts)54 bcm_xtlv_hdr_size(bcm_xtlv_opts_t opts)
55 {
56 int len = (int)OFFSETOF(bcm_xtlv_t, data); /* nominal */
57 if (opts & BCM_XTLV_OPTION_LENU8) --len;
58 if (opts & BCM_XTLV_OPTION_IDU8) --len;
59
60 return len;
61 }
62
63 bool
bcm_valid_xtlv(const bcm_xtlv_t * elt,int buf_len,bcm_xtlv_opts_t opts)64 bcm_valid_xtlv(const bcm_xtlv_t *elt, int buf_len, bcm_xtlv_opts_t opts)
65 {
66 return elt != NULL &&
67 buf_len >= bcm_xtlv_hdr_size(opts) &&
68 buf_len >= bcm_xtlv_size(elt, opts);
69 }
70
71 int
bcm_xtlv_size_for_data(int dlen,bcm_xtlv_opts_t opts)72 bcm_xtlv_size_for_data(int dlen, bcm_xtlv_opts_t opts)
73 {
74 int hsz;
75
76 hsz = bcm_xtlv_hdr_size(opts);
77 return ((opts & BCM_XTLV_OPTION_ALIGN32) ? ALIGN_SIZE(dlen + hsz, 4)
78 : (dlen + hsz));
79 }
80
81 int
bcm_xtlv_size(const bcm_xtlv_t * elt,bcm_xtlv_opts_t opts)82 bcm_xtlv_size(const bcm_xtlv_t *elt, bcm_xtlv_opts_t opts)
83 {
84 int size; /* size including header, data, and any pad */
85 int len; /* length wthout padding */
86
87 len = BCM_XTLV_LEN_EX(elt, opts);
88 size = bcm_xtlv_size_for_data(len, opts);
89 return size;
90 }
91
92 int
bcm_xtlv_len(const bcm_xtlv_t * elt,bcm_xtlv_opts_t opts)93 bcm_xtlv_len(const bcm_xtlv_t *elt, bcm_xtlv_opts_t opts)
94 {
95 const uint8 *lenp;
96 int len;
97
98 lenp = (const uint8 *)&elt->len; /* nominal */
99 if (opts & BCM_XTLV_OPTION_IDU8) {
100 --lenp;
101 }
102
103 if (opts & BCM_XTLV_OPTION_LENU8) {
104 len = *lenp;
105 } else if (opts & BCM_XTLV_OPTION_LENBE) {
106 len = (uint32)hton16(elt->len);
107 } else {
108 len = ltoh16_ua(lenp);
109 }
110
111 return len;
112 }
113
114 int
bcm_xtlv_id(const bcm_xtlv_t * elt,bcm_xtlv_opts_t opts)115 bcm_xtlv_id(const bcm_xtlv_t *elt, bcm_xtlv_opts_t opts)
116 {
117 int id = 0;
118 if (opts & BCM_XTLV_OPTION_IDU8) {
119 id = *(const uint8 *)elt;
120 } else if (opts & BCM_XTLV_OPTION_IDBE) {
121 id = (uint32)hton16(elt->id);
122 } else {
123 id = ltoh16_ua((const uint8 *)elt);
124 }
125
126 return id;
127 }
128
129 bcm_xtlv_t *
bcm_next_xtlv(const bcm_xtlv_t * elt,int * buflen,bcm_xtlv_opts_t opts)130 bcm_next_xtlv(const bcm_xtlv_t *elt, int *buflen, bcm_xtlv_opts_t opts)
131 {
132 int sz;
133 /* advance to next elt */
134 sz = BCM_XTLV_SIZE_EX(elt, opts);
135 elt = (const bcm_xtlv_t*)((const uint8 *)elt + sz);
136 *buflen -= sz;
137
138 /* validate next elt */
139 if (!bcm_valid_xtlv(elt, *buflen, opts))
140 return NULL;
141
142 GCC_DIAGNOSTIC_PUSH_SUPPRESS_CAST();
143 return (bcm_xtlv_t *)(elt);
144 GCC_DIAGNOSTIC_POP();
145 }
146
147 int
bcm_xtlv_buf_init(bcm_xtlvbuf_t * tlv_buf,uint8 * buf,uint16 len,bcm_xtlv_opts_t opts)148 bcm_xtlv_buf_init(bcm_xtlvbuf_t *tlv_buf, uint8 *buf, uint16 len, bcm_xtlv_opts_t opts)
149 {
150 if (!tlv_buf || !buf || !len)
151 return BCME_BADARG;
152
153 tlv_buf->opts = opts;
154 tlv_buf->size = len;
155 tlv_buf->head = buf;
156 tlv_buf->buf = buf;
157 return BCME_OK;
158 }
159
160 uint16
bcm_xtlv_buf_len(bcm_xtlvbuf_t * tbuf)161 bcm_xtlv_buf_len(bcm_xtlvbuf_t *tbuf)
162 {
163 uint16 len;
164
165 if (tbuf)
166 len = (uint16)(tbuf->buf - tbuf->head);
167 else
168 len = 0;
169
170 return len;
171 }
172
173 uint16
bcm_xtlv_buf_rlen(bcm_xtlvbuf_t * tbuf)174 bcm_xtlv_buf_rlen(bcm_xtlvbuf_t *tbuf)
175 {
176 uint16 rlen;
177 if (tbuf)
178 rlen = tbuf->size - bcm_xtlv_buf_len(tbuf);
179 else
180 rlen = 0;
181
182 return rlen;
183 }
184
185 uint8 *
bcm_xtlv_buf(bcm_xtlvbuf_t * tbuf)186 bcm_xtlv_buf(bcm_xtlvbuf_t *tbuf)
187 {
188 return tbuf ? tbuf->buf : NULL;
189 }
190
191 uint8 *
bcm_xtlv_head(bcm_xtlvbuf_t * tbuf)192 bcm_xtlv_head(bcm_xtlvbuf_t *tbuf)
193 {
194 return tbuf ? tbuf->head : NULL;
195 }
196
197 void
bcm_xtlv_pack_xtlv(bcm_xtlv_t * xtlv,uint16 type,uint16 len,const uint8 * data,bcm_xtlv_opts_t opts)198 bcm_xtlv_pack_xtlv(bcm_xtlv_t *xtlv, uint16 type, uint16 len, const uint8 *data,
199 bcm_xtlv_opts_t opts)
200 {
201 uint8 *data_buf;
202 bcm_xtlv_opts_t mask = BCM_XTLV_OPTION_IDU8 | BCM_XTLV_OPTION_LENU8;
203
204 if (!(opts & mask)) { /* default */
205 uint8 *idp = (uint8 *)xtlv;
206 uint8 *lenp = idp + sizeof(xtlv->id);
207 htol16_ua_store(type, idp);
208 htol16_ua_store(len, lenp);
209 data_buf = lenp + sizeof(uint16);
210 } else if ((opts & mask) == mask) { /* u8 id and u8 len */
211 uint8 *idp = (uint8 *)xtlv;
212 uint8 *lenp = idp + 1;
213 *idp = (uint8)type;
214 *lenp = (uint8)len;
215 data_buf = lenp + sizeof(uint8);
216 } else if (opts & BCM_XTLV_OPTION_IDU8) { /* u8 id, u16 len */
217 uint8 *idp = (uint8 *)xtlv;
218 uint8 *lenp = idp + 1;
219 *idp = (uint8)type;
220 htol16_ua_store(len, lenp);
221 data_buf = lenp + sizeof(uint16);
222 } else if (opts & BCM_XTLV_OPTION_LENU8) { /* u16 id, u8 len */
223 uint8 *idp = (uint8 *)xtlv;
224 uint8 *lenp = idp + sizeof(uint16);
225 htol16_ua_store(type, idp);
226 *lenp = (uint8)len;
227 data_buf = lenp + sizeof(uint8);
228 } else {
229 bool Unexpected_xtlv_option = TRUE;
230 BCM_REFERENCE(Unexpected_xtlv_option);
231 ASSERT(!Unexpected_xtlv_option);
232 return;
233 }
234
235 if (opts & BCM_XTLV_OPTION_LENU8) {
236 ASSERT(len <= 0x00ff);
237 len &= 0xff;
238 }
239
240 if (data != NULL)
241 memcpy(data_buf, data, len);
242 }
243
244 /* xtlv header is always packed in LE order */
245 void
bcm_xtlv_unpack_xtlv(const bcm_xtlv_t * xtlv,uint16 * type,uint16 * len,const uint8 ** data,bcm_xtlv_opts_t opts)246 bcm_xtlv_unpack_xtlv(const bcm_xtlv_t *xtlv, uint16 *type, uint16 *len,
247 const uint8 **data, bcm_xtlv_opts_t opts)
248 {
249 if (type)
250 *type = (uint16)bcm_xtlv_id(xtlv, opts);
251 if (len)
252 *len = (uint16)bcm_xtlv_len(xtlv, opts);
253 if (data)
254 *data = (const uint8 *)xtlv + BCM_XTLV_HDR_SIZE_EX(opts);
255 }
256
257 int
bcm_xtlv_put_data(bcm_xtlvbuf_t * tbuf,uint16 type,const uint8 * data,int n)258 bcm_xtlv_put_data(bcm_xtlvbuf_t *tbuf, uint16 type, const uint8 *data, int n)
259 {
260 bcm_xtlv_t *xtlv;
261 int size;
262
263 if (tbuf == NULL)
264 return BCME_BADARG;
265
266 size = bcm_xtlv_size_for_data(n, tbuf->opts);
267 if (bcm_xtlv_buf_rlen(tbuf) < size)
268 return BCME_NOMEM;
269
270 xtlv = (bcm_xtlv_t *)bcm_xtlv_buf(tbuf);
271 bcm_xtlv_pack_xtlv(xtlv, type, (uint16)n, data, tbuf->opts);
272 tbuf->buf += size; /* note: data may be NULL, reserves space */
273 return BCME_OK;
274 }
275
276 static int
bcm_xtlv_put_int(bcm_xtlvbuf_t * tbuf,uint16 type,const uint8 * data,int n,int int_sz)277 bcm_xtlv_put_int(bcm_xtlvbuf_t *tbuf, uint16 type, const uint8 *data, int n, int int_sz)
278 {
279 bcm_xtlv_t *xtlv;
280 int xtlv_len;
281 uint8 *xtlv_data;
282 int err = BCME_OK;
283
284 if (tbuf == NULL) {
285 err = BCME_BADARG;
286 goto done;
287 }
288
289 xtlv = (bcm_xtlv_t *)bcm_xtlv_buf(tbuf);
290
291 /* put type and length in xtlv and reserve data space */
292 xtlv_len = n * int_sz;
293 err = bcm_xtlv_put_data(tbuf, type, NULL, xtlv_len);
294 if (err != BCME_OK)
295 goto done;
296
297 xtlv_data = (uint8 *)xtlv + bcm_xtlv_hdr_size(tbuf->opts);
298
299 /* write data w/ little-endianness into buffer - single loop, aligned access */
300 for (; n != 0; --n, xtlv_data += int_sz, data += int_sz) {
301 switch (int_sz) {
302 case sizeof(uint8):
303 break;
304 case sizeof(uint16):
305 {
306 uint16 v = load16_ua(data);
307 htol16_ua_store(v, xtlv_data);
308 break;
309 }
310 case sizeof(uint32):
311 {
312 uint32 v = load32_ua(data);
313 htol32_ua_store(v, xtlv_data);
314 break;
315 }
316 case sizeof(uint64):
317 {
318 uint64 v = load64_ua(data);
319 htol64_ua_store(v, xtlv_data);
320 break;
321 }
322 default:
323 err = BCME_UNSUPPORTED;
324 goto done;
325 }
326 }
327
328 done:
329 return err;
330 }
331
332 int
bcm_xtlv_put16(bcm_xtlvbuf_t * tbuf,uint16 type,const uint16 * data,int n)333 bcm_xtlv_put16(bcm_xtlvbuf_t *tbuf, uint16 type, const uint16 *data, int n)
334 {
335 return bcm_xtlv_put_int(tbuf, type, (const uint8 *)data, n, sizeof(uint16));
336 }
337
338 int
bcm_xtlv_put32(bcm_xtlvbuf_t * tbuf,uint16 type,const uint32 * data,int n)339 bcm_xtlv_put32(bcm_xtlvbuf_t *tbuf, uint16 type, const uint32 *data, int n)
340 {
341 return bcm_xtlv_put_int(tbuf, type, (const uint8 *)data, n, sizeof(uint32));
342 }
343
344 int
bcm_xtlv_put64(bcm_xtlvbuf_t * tbuf,uint16 type,const uint64 * data,int n)345 bcm_xtlv_put64(bcm_xtlvbuf_t *tbuf, uint16 type, const uint64 *data, int n)
346 {
347 return bcm_xtlv_put_int(tbuf, type, (const uint8 *)data, n, sizeof(uint64));
348 }
349
350 /*
351 * upacks xtlv record from buf checks the type
352 * copies data to callers buffer
353 * advances tlv pointer to next record
354 * caller's resposible for dst space check
355 */
356 int
bcm_unpack_xtlv_entry(const uint8 ** tlv_buf,uint16 xpct_type,uint16 xpct_len,uint8 * dst_data,bcm_xtlv_opts_t opts)357 bcm_unpack_xtlv_entry(const uint8 **tlv_buf, uint16 xpct_type, uint16 xpct_len,
358 uint8 *dst_data, bcm_xtlv_opts_t opts)
359 {
360 const bcm_xtlv_t *ptlv = (const bcm_xtlv_t *)*tlv_buf;
361 uint16 len;
362 uint16 type;
363 const uint8 *data;
364
365 ASSERT(ptlv);
366
367 bcm_xtlv_unpack_xtlv(ptlv, &type, &len, &data, opts);
368 if (len) {
369 if ((type != xpct_type) || (len > xpct_len))
370 return BCME_BADARG;
371 if (dst_data && data)
372 memcpy(dst_data, data, len); /* copy data to dst */
373 }
374
375 *tlv_buf += BCM_XTLV_SIZE_EX(ptlv, opts);
376 return BCME_OK;
377 }
378
379 /*
380 * packs user data into tlv record and advances tlv pointer to next xtlv slot
381 * buflen is used for tlv_buf space check
382 */
383 int
bcm_pack_xtlv_entry(uint8 ** tlv_buf,uint16 * buflen,uint16 type,uint16 len,const uint8 * src_data,bcm_xtlv_opts_t opts)384 bcm_pack_xtlv_entry(uint8 **tlv_buf, uint16 *buflen, uint16 type, uint16 len,
385 const uint8 *src_data, bcm_xtlv_opts_t opts)
386 {
387 bcm_xtlv_t *ptlv = (bcm_xtlv_t *)*tlv_buf;
388 int size;
389
390 ASSERT(ptlv);
391
392 size = bcm_xtlv_size_for_data(len, opts);
393
394 /* copy data from tlv buffer to dst provided by user */
395 if (size > *buflen)
396 return BCME_BADLEN;
397
398 bcm_xtlv_pack_xtlv(ptlv, type, len, src_data, opts);
399
400 /* advance callers pointer to tlv buff */
401 *tlv_buf = (uint8*)(*tlv_buf) + size;
402 /* decrement the len */
403 *buflen -= (uint16)size;
404 return BCME_OK;
405 }
406
407 /*
408 * unpack all xtlv records from the issue a callback
409 * to set function one call per found tlv record
410 */
411 int
bcm_unpack_xtlv_buf(void * ctx,const uint8 * tlv_buf,uint16 buflen,bcm_xtlv_opts_t opts,bcm_xtlv_unpack_cbfn_t * cbfn)412 bcm_unpack_xtlv_buf(void *ctx, const uint8 *tlv_buf, uint16 buflen, bcm_xtlv_opts_t opts,
413 bcm_xtlv_unpack_cbfn_t *cbfn)
414 {
415 uint16 len;
416 uint16 type;
417 int res = BCME_OK;
418 int size;
419 const bcm_xtlv_t *ptlv;
420 int sbuflen = buflen;
421 const uint8 *data;
422 int hdr_size;
423
424 ASSERT(!buflen || tlv_buf);
425 ASSERT(!buflen || cbfn);
426
427 hdr_size = BCM_XTLV_HDR_SIZE_EX(opts);
428 while (sbuflen >= hdr_size) {
429 ptlv = (const bcm_xtlv_t *)tlv_buf;
430
431 bcm_xtlv_unpack_xtlv(ptlv, &type, &len, &data, opts);
432 size = bcm_xtlv_size_for_data(len, opts);
433
434 sbuflen -= size;
435 if (sbuflen < 0) /* check for buffer overrun */
436 break;
437
438 if ((res = cbfn(ctx, data, type, len)) != BCME_OK)
439 break;
440 tlv_buf += size;
441 }
442 return res;
443 }
444
445 int
bcm_pack_xtlv_buf(void * ctx,uint8 * tlv_buf,uint16 buflen,bcm_xtlv_opts_t opts,bcm_pack_xtlv_next_info_cbfn_t get_next,bcm_pack_xtlv_pack_next_cbfn_t pack_next,int * outlen)446 bcm_pack_xtlv_buf(void *ctx, uint8 *tlv_buf, uint16 buflen, bcm_xtlv_opts_t opts,
447 bcm_pack_xtlv_next_info_cbfn_t get_next, bcm_pack_xtlv_pack_next_cbfn_t pack_next,
448 int *outlen)
449 {
450 int res = BCME_OK;
451 uint16 tlv_id;
452 uint16 tlv_len;
453 uint8 *startp;
454 uint8 *endp;
455 uint8 *buf;
456 bool more;
457 int size;
458 int hdr_size;
459
460 ASSERT(get_next && pack_next);
461
462 buf = tlv_buf;
463 startp = buf;
464 endp = (uint8 *)buf + buflen;
465 more = TRUE;
466 hdr_size = BCM_XTLV_HDR_SIZE_EX(opts);
467
468 while (more && (buf < endp)) {
469 more = get_next(ctx, &tlv_id, &tlv_len);
470 size = bcm_xtlv_size_for_data(tlv_len, opts);
471 if ((buf + size) > endp) {
472 res = BCME_BUFTOOSHORT;
473 goto done;
474 }
475
476 bcm_xtlv_pack_xtlv((bcm_xtlv_t *)buf, tlv_id, tlv_len, NULL, opts);
477 pack_next(ctx, tlv_id, tlv_len, buf + hdr_size);
478 buf += size;
479 }
480
481 if (more)
482 res = BCME_BUFTOOSHORT;
483
484 done:
485 if (outlen) {
486 *outlen = (int)(buf - startp);
487 }
488 return res;
489 }
490
491 /*
492 * pack xtlv buffer from memory according to xtlv_desc_t
493 */
494 int
bcm_pack_xtlv_buf_from_mem(uint8 ** tlv_buf,uint16 * buflen,const xtlv_desc_t * items,bcm_xtlv_opts_t opts)495 bcm_pack_xtlv_buf_from_mem(uint8 **tlv_buf, uint16 *buflen, const xtlv_desc_t *items,
496 bcm_xtlv_opts_t opts)
497 {
498 int res = BCME_OK;
499 uint8 *ptlv = *tlv_buf;
500
501 while (items->type != 0) {
502 if (items->len && items->ptr) {
503 res = bcm_pack_xtlv_entry(&ptlv, buflen, items->type,
504 items->len, items->ptr, opts);
505 if (res != BCME_OK)
506 break;
507 }
508 items++;
509 }
510
511 *tlv_buf = ptlv; /* update the external pointer */
512 return res;
513 }
514
515 /*
516 * unpack xtlv buffer to memory according to xtlv_desc_t
517 *
518 */
519 int
bcm_unpack_xtlv_buf_to_mem(uint8 * tlv_buf,int * buflen,xtlv_desc_t * items,bcm_xtlv_opts_t opts)520 bcm_unpack_xtlv_buf_to_mem(uint8 *tlv_buf, int *buflen, xtlv_desc_t *items,
521 bcm_xtlv_opts_t opts)
522 {
523 int res = BCME_OK;
524 bcm_xtlv_t *elt;
525
526 elt = bcm_valid_xtlv((bcm_xtlv_t *)tlv_buf, *buflen, opts) ? (bcm_xtlv_t *)tlv_buf : NULL;
527 if (!elt || !items) {
528 res = BCME_BADARG;
529 return res;
530 }
531
532 for (; elt != NULL && res == BCME_OK; elt = bcm_next_xtlv(elt, buflen, opts)) {
533 /* find matches in desc_t items */
534 xtlv_desc_t *dst_desc = items;
535 uint16 len, type;
536 const uint8 *data;
537
538 bcm_xtlv_unpack_xtlv(elt, &type, &len, &data, opts);
539 while (dst_desc->type != 0) {
540 if (type == dst_desc->type) {
541 if (len != dst_desc->len) {
542 res = BCME_BADLEN;
543 } else {
544 memcpy(dst_desc->ptr, data, len);
545 }
546 break;
547 }
548 dst_desc++;
549 }
550 }
551
552 if (res == BCME_OK && *buflen != 0)
553 res = BCME_BUFTOOSHORT;
554
555 return res;
556 }
557
558 /*
559 * return data pointer of a given ID from xtlv buffer.
560 * If the specified xTLV ID is found, on return *datalen will contain
561 * the the data length of the xTLV ID.
562 */
563 const uint8*
bcm_get_data_from_xtlv_buf(const uint8 * tlv_buf,uint16 buflen,uint16 id,uint16 * datalen,bcm_xtlv_opts_t opts)564 bcm_get_data_from_xtlv_buf(const uint8 *tlv_buf, uint16 buflen, uint16 id,
565 uint16 *datalen, bcm_xtlv_opts_t opts)
566 {
567 const uint8 *retptr = NULL;
568 uint16 type, len;
569 int size;
570 const bcm_xtlv_t *ptlv;
571 int sbuflen = buflen;
572 const uint8 *data;
573 int hdr_size;
574
575 hdr_size = BCM_XTLV_HDR_SIZE_EX(opts);
576
577 /* Init the datalength */
578 if (datalen) {
579 *datalen = 0;
580 }
581 while (sbuflen >= hdr_size) {
582 ptlv = (const bcm_xtlv_t *)tlv_buf;
583 bcm_xtlv_unpack_xtlv(ptlv, &type, &len, &data, opts);
584
585 size = bcm_xtlv_size_for_data(len, opts);
586 sbuflen -= size;
587 if (sbuflen < 0) /* buffer overrun? */
588 break;
589
590 if (id == type) {
591 retptr = data;
592 if (datalen)
593 *datalen = len;
594 break;
595 }
596
597 tlv_buf += size;
598 }
599
600 return retptr;
601 }
602
603 bcm_xtlv_t*
bcm_xtlv_bcopy(const bcm_xtlv_t * src,bcm_xtlv_t * dst,int src_buf_len,int dst_buf_len,bcm_xtlv_opts_t opts)604 bcm_xtlv_bcopy(const bcm_xtlv_t *src, bcm_xtlv_t *dst,
605 int src_buf_len, int dst_buf_len, bcm_xtlv_opts_t opts)
606 {
607 bcm_xtlv_t *dst_next = NULL;
608 src = (src && bcm_valid_xtlv(src, src_buf_len, opts)) ? src : NULL;
609 if (src && dst) {
610 uint16 type;
611 uint16 len;
612 const uint8 *data;
613 int size;
614 bcm_xtlv_unpack_xtlv(src, &type, &len, &data, opts);
615 size = bcm_xtlv_size_for_data(len, opts);
616 if (size <= dst_buf_len) {
617 bcm_xtlv_pack_xtlv(dst, type, len, data, opts);
618 dst_next = (bcm_xtlv_t *)((uint8 *)dst + size);
619 }
620 }
621
622 return dst_next;
623 }
624