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