• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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