2 * Driver O/S-independent utility routines
4 * Copyright (C) 1999-2019, Broadcom.
6 * Unless you and Broadcom execute a separate written software license
7 * agreement governing use of this software, this software is licensed to you
8 * under the terms of the GNU General Public License version 2 (the "GPL"),
9 * available at http://www.broadcom.com/licenses/GPLv2.php, with the
10 * following added to such license:
12 * As a special exception, the copyright holders of this software give you
13 * permission to link this software with independent modules, and to copy and
14 * distribute the resulting executable under terms of your choice, provided that
15 * you also meet, for each linked independent module, the terms and conditions of
16 * the license of that module. An independent module is a module which is not
17 * derived from this software. The special exception does not apply to any
18 * modifications of the software.
20 * Notwithstanding the above, under no circumstances may you combine this
21 * software in any way with any other Broadcom software provided under a license
22 * other than the GPL, without Broadcom's express prior written consent.
25 * <<Broadcom-WL-IPTag/Open:>>
27 * $Id: bcmxtlv.c 788740 2018-11-13 21:45:01Z $
39 #else /* !BCMDRIVER */
46 #endif /* !BCMDRIVER */
49 #include <bcmendian.h>
53 bcm_xtlv_hdr_size(bcm_xtlv_opts_t opts
)
55 int len
= (int)OFFSETOF(bcm_xtlv_t
, data
); /* nominal */
56 if (opts
& BCM_XTLV_OPTION_LENU8
) --len
;
57 if (opts
& BCM_XTLV_OPTION_IDU8
) --len
;
63 bcm_valid_xtlv(const bcm_xtlv_t
*elt
, int buf_len
, bcm_xtlv_opts_t opts
)
66 buf_len
>= bcm_xtlv_hdr_size(opts
) &&
67 buf_len
>= bcm_xtlv_size(elt
, opts
);
71 bcm_xtlv_size_for_data(int dlen
, bcm_xtlv_opts_t opts
)
75 hsz
= bcm_xtlv_hdr_size(opts
);
76 return ((opts
& BCM_XTLV_OPTION_ALIGN32
) ? ALIGN_SIZE(dlen
+ hsz
, 4)
81 bcm_xtlv_size(const bcm_xtlv_t
*elt
, bcm_xtlv_opts_t opts
)
83 int size
; /* size including header, data, and any pad */
84 int len
; /* length wthout padding */
86 len
= BCM_XTLV_LEN_EX(elt
, opts
);
87 size
= bcm_xtlv_size_for_data(len
, opts
);
92 bcm_xtlv_len(const bcm_xtlv_t
*elt
, bcm_xtlv_opts_t opts
)
97 lenp
= (const uint8
*)&elt
->len
; /* nominal */
98 if (opts
& BCM_XTLV_OPTION_IDU8
) {
102 if (opts
& BCM_XTLV_OPTION_LENU8
) {
104 } else if (opts
& BCM_XTLV_OPTION_LENBE
) {
105 len
= (uint32
)hton16(elt
->len
);
107 len
= ltoh16_ua(lenp
);
114 bcm_xtlv_id(const bcm_xtlv_t
*elt
, bcm_xtlv_opts_t opts
)
117 if (opts
& BCM_XTLV_OPTION_IDU8
) {
118 id
= *(const uint8
*)elt
;
119 } else if (opts
& BCM_XTLV_OPTION_IDBE
) {
120 id
= (uint32
)hton16(elt
->id
);
122 id
= ltoh16_ua((const uint8
*)elt
);
129 bcm_next_xtlv(const bcm_xtlv_t
*elt
, int *buflen
, bcm_xtlv_opts_t opts
)
132 /* advance to next elt */
133 sz
= BCM_XTLV_SIZE_EX(elt
, opts
);
134 elt
= (const bcm_xtlv_t
*)((const uint8
*)elt
+ sz
);
137 /* validate next elt */
138 if (!bcm_valid_xtlv(elt
, *buflen
, opts
))
141 GCC_DIAGNOSTIC_PUSH_SUPPRESS_CAST();
142 return (bcm_xtlv_t
*)(elt
);
143 GCC_DIAGNOSTIC_POP();
147 bcm_xtlv_buf_init(bcm_xtlvbuf_t
*tlv_buf
, uint8
*buf
, uint16 len
, bcm_xtlv_opts_t opts
)
149 if (!tlv_buf
|| !buf
|| !len
)
152 tlv_buf
->opts
= opts
;
160 bcm_xtlv_buf_len(bcm_xtlvbuf_t
*tbuf
)
165 len
= (uint16
)(tbuf
->buf
- tbuf
->head
);
173 bcm_xtlv_buf_rlen(bcm_xtlvbuf_t
*tbuf
)
177 rlen
= tbuf
->size
- bcm_xtlv_buf_len(tbuf
);
185 bcm_xtlv_buf(bcm_xtlvbuf_t
*tbuf
)
187 return tbuf
? tbuf
->buf
: NULL
;
191 bcm_xtlv_head(bcm_xtlvbuf_t
*tbuf
)
193 return tbuf
? tbuf
->head
: NULL
;
197 bcm_xtlv_pack_xtlv(bcm_xtlv_t
*xtlv
, uint16 type
, uint16 len
, const uint8
*data
,
198 bcm_xtlv_opts_t opts
)
201 bcm_xtlv_opts_t mask
= BCM_XTLV_OPTION_IDU8
| BCM_XTLV_OPTION_LENU8
;
203 if (!(opts
& mask
)) { /* default */
204 uint8
*idp
= (uint8
*)xtlv
;
205 uint8
*lenp
= idp
+ sizeof(xtlv
->id
);
206 htol16_ua_store(type
, idp
);
207 htol16_ua_store(len
, lenp
);
208 data_buf
= lenp
+ sizeof(uint16
);
209 } else if ((opts
& mask
) == mask
) { /* u8 id and u8 len */
210 uint8
*idp
= (uint8
*)xtlv
;
211 uint8
*lenp
= idp
+ 1;
214 data_buf
= lenp
+ sizeof(uint8
);
215 } else if (opts
& BCM_XTLV_OPTION_IDU8
) { /* u8 id, u16 len */
216 uint8
*idp
= (uint8
*)xtlv
;
217 uint8
*lenp
= idp
+ 1;
219 htol16_ua_store(len
, lenp
);
220 data_buf
= lenp
+ sizeof(uint16
);
221 } else if (opts
& BCM_XTLV_OPTION_LENU8
) { /* u16 id, u8 len */
222 uint8
*idp
= (uint8
*)xtlv
;
223 uint8
*lenp
= idp
+ sizeof(uint16
);
224 htol16_ua_store(type
, idp
);
226 data_buf
= lenp
+ sizeof(uint8
);
228 bool Unexpected_xtlv_option
= TRUE
;
229 BCM_REFERENCE(Unexpected_xtlv_option
);
230 ASSERT(!Unexpected_xtlv_option
);
234 if (opts
& BCM_XTLV_OPTION_LENU8
) {
235 ASSERT(len
<= 0x00ff);
240 memcpy(data_buf
, data
, len
);
243 /* xtlv header is always packed in LE order */
245 bcm_xtlv_unpack_xtlv(const bcm_xtlv_t
*xtlv
, uint16
*type
, uint16
*len
,
246 const uint8
**data
, bcm_xtlv_opts_t opts
)
249 *type
= (uint16
)bcm_xtlv_id(xtlv
, opts
);
251 *len
= (uint16
)bcm_xtlv_len(xtlv
, opts
);
253 *data
= (const uint8
*)xtlv
+ BCM_XTLV_HDR_SIZE_EX(opts
);
257 bcm_xtlv_put_data(bcm_xtlvbuf_t
*tbuf
, uint16 type
, const uint8
*data
, int n
)
265 size
= bcm_xtlv_size_for_data(n
, tbuf
->opts
);
266 if (bcm_xtlv_buf_rlen(tbuf
) < size
)
269 xtlv
= (bcm_xtlv_t
*)bcm_xtlv_buf(tbuf
);
270 bcm_xtlv_pack_xtlv(xtlv
, type
, (uint16
)n
, data
, tbuf
->opts
);
271 tbuf
->buf
+= size
; /* note: data may be NULL, reserves space */
276 bcm_xtlv_put_int(bcm_xtlvbuf_t
*tbuf
, uint16 type
, const uint8
*data
, int n
, int int_sz
)
288 xtlv
= (bcm_xtlv_t
*)bcm_xtlv_buf(tbuf
);
290 /* put type and length in xtlv and reserve data space */
291 xtlv_len
= n
* int_sz
;
292 err
= bcm_xtlv_put_data(tbuf
, type
, NULL
, xtlv_len
);
296 xtlv_data
= (uint8
*)xtlv
+ bcm_xtlv_hdr_size(tbuf
->opts
);
298 /* write data w/ little-endianness into buffer - single loop, aligned access */
299 for (; n
!= 0; --n
, xtlv_data
+= int_sz
, data
+= int_sz
) {
305 uint16 v
= load16_ua(data
);
306 htol16_ua_store(v
, xtlv_data
);
311 uint32 v
= load32_ua(data
);
312 htol32_ua_store(v
, xtlv_data
);
317 uint64 v
= load64_ua(data
);
318 htol64_ua_store(v
, xtlv_data
);
322 err
= BCME_UNSUPPORTED
;
332 bcm_xtlv_put16(bcm_xtlvbuf_t
*tbuf
, uint16 type
, const uint16
*data
, int n
)
334 return bcm_xtlv_put_int(tbuf
, type
, (const uint8
*)data
, n
, sizeof(uint16
));
338 bcm_xtlv_put32(bcm_xtlvbuf_t
*tbuf
, uint16 type
, const uint32
*data
, int n
)
340 return bcm_xtlv_put_int(tbuf
, type
, (const uint8
*)data
, n
, sizeof(uint32
));
344 bcm_xtlv_put64(bcm_xtlvbuf_t
*tbuf
, uint16 type
, const uint64
*data
, int n
)
346 return bcm_xtlv_put_int(tbuf
, type
, (const uint8
*)data
, n
, sizeof(uint64
));
350 * upacks xtlv record from buf checks the type
351 * copies data to callers buffer
352 * advances tlv pointer to next record
353 * caller's resposible for dst space check
356 bcm_unpack_xtlv_entry(const uint8
**tlv_buf
, uint16 xpct_type
, uint16 xpct_len
,
357 uint8
*dst_data
, bcm_xtlv_opts_t opts
)
359 const bcm_xtlv_t
*ptlv
= (const bcm_xtlv_t
*)*tlv_buf
;
366 bcm_xtlv_unpack_xtlv(ptlv
, &type
, &len
, &data
, opts
);
368 if ((type
!= xpct_type
) || (len
> xpct_len
))
370 if (dst_data
&& data
)
371 memcpy(dst_data
, data
, len
); /* copy data to dst */
374 *tlv_buf
+= BCM_XTLV_SIZE_EX(ptlv
, opts
);
379 * packs user data into tlv record and advances tlv pointer to next xtlv slot
380 * buflen is used for tlv_buf space check
383 bcm_pack_xtlv_entry(uint8
**tlv_buf
, uint16
*buflen
, uint16 type
, uint16 len
,
384 const uint8
*src_data
, bcm_xtlv_opts_t opts
)
386 bcm_xtlv_t
*ptlv
= (bcm_xtlv_t
*)*tlv_buf
;
391 size
= bcm_xtlv_size_for_data(len
, opts
);
393 /* copy data from tlv buffer to dst provided by user */
397 bcm_xtlv_pack_xtlv(ptlv
, type
, len
, src_data
, opts
);
399 /* advance callers pointer to tlv buff */
400 *tlv_buf
= (uint8
*)(*tlv_buf
) + size
;
401 /* decrement the len */
402 *buflen
-= (uint16
)size
;
407 * unpack all xtlv records from the issue a callback
408 * to set function one call per found tlv record
411 bcm_unpack_xtlv_buf(void *ctx
, const uint8
*tlv_buf
, uint16 buflen
, bcm_xtlv_opts_t opts
,
412 bcm_xtlv_unpack_cbfn_t
*cbfn
)
418 const bcm_xtlv_t
*ptlv
;
419 int sbuflen
= buflen
;
423 ASSERT(!buflen
|| tlv_buf
);
424 ASSERT(!buflen
|| cbfn
);
426 hdr_size
= BCM_XTLV_HDR_SIZE_EX(opts
);
427 while (sbuflen
>= hdr_size
) {
428 ptlv
= (const bcm_xtlv_t
*)tlv_buf
;
430 bcm_xtlv_unpack_xtlv(ptlv
, &type
, &len
, &data
, opts
);
431 size
= bcm_xtlv_size_for_data(len
, opts
);
434 if (sbuflen
< 0) /* check for buffer overrun */
437 if ((res
= cbfn(ctx
, data
, type
, len
)) != BCME_OK
)
445 bcm_pack_xtlv_buf(void *ctx
, uint8
*tlv_buf
, uint16 buflen
, bcm_xtlv_opts_t opts
,
446 bcm_pack_xtlv_next_info_cbfn_t get_next
, bcm_pack_xtlv_pack_next_cbfn_t pack_next
,
459 ASSERT(get_next
&& pack_next
);
463 endp
= (uint8
*)buf
+ buflen
;
465 hdr_size
= BCM_XTLV_HDR_SIZE_EX(opts
);
467 while (more
&& (buf
< endp
)) {
468 more
= get_next(ctx
, &tlv_id
, &tlv_len
);
469 size
= bcm_xtlv_size_for_data(tlv_len
, opts
);
470 if ((buf
+ size
) > endp
) {
471 res
= BCME_BUFTOOSHORT
;
475 bcm_xtlv_pack_xtlv((bcm_xtlv_t
*)buf
, tlv_id
, tlv_len
, NULL
, opts
);
476 pack_next(ctx
, tlv_id
, tlv_len
, buf
+ hdr_size
);
481 res
= BCME_BUFTOOSHORT
;
485 *outlen
= (int)(buf
- startp
);
491 * pack xtlv buffer from memory according to xtlv_desc_t
494 bcm_pack_xtlv_buf_from_mem(uint8
**tlv_buf
, uint16
*buflen
, const xtlv_desc_t
*items
,
495 bcm_xtlv_opts_t opts
)
498 uint8
*ptlv
= *tlv_buf
;
500 while (items
->type
!= 0) {
501 if (items
->len
&& items
->ptr
) {
502 res
= bcm_pack_xtlv_entry(&ptlv
, buflen
, items
->type
,
503 items
->len
, items
->ptr
, opts
);
510 *tlv_buf
= ptlv
; /* update the external pointer */
515 * unpack xtlv buffer to memory according to xtlv_desc_t
519 bcm_unpack_xtlv_buf_to_mem(uint8
*tlv_buf
, int *buflen
, xtlv_desc_t
*items
,
520 bcm_xtlv_opts_t opts
)
525 elt
= bcm_valid_xtlv((bcm_xtlv_t
*)tlv_buf
, *buflen
, opts
) ? (bcm_xtlv_t
*)tlv_buf
: NULL
;
526 if (!elt
|| !items
) {
531 for (; elt
!= NULL
&& res
== BCME_OK
; elt
= bcm_next_xtlv(elt
, buflen
, opts
)) {
532 /* find matches in desc_t items */
533 xtlv_desc_t
*dst_desc
= items
;
537 bcm_xtlv_unpack_xtlv(elt
, &type
, &len
, &data
, opts
);
538 while (dst_desc
->type
!= 0) {
539 if (type
== dst_desc
->type
) {
540 if (len
!= dst_desc
->len
) {
543 memcpy(dst_desc
->ptr
, data
, len
);
551 if (res
== BCME_OK
&& *buflen
!= 0)
552 res
= BCME_BUFTOOSHORT
;
558 * return data pointer of a given ID from xtlv buffer.
559 * If the specified xTLV ID is found, on return *datalen will contain
560 * the the data length of the xTLV ID.
563 bcm_get_data_from_xtlv_buf(const uint8
*tlv_buf
, uint16 buflen
, uint16 id
,
564 uint16
*datalen
, bcm_xtlv_opts_t opts
)
566 const uint8
*retptr
= NULL
;
569 const bcm_xtlv_t
*ptlv
;
570 int sbuflen
= buflen
;
574 hdr_size
= BCM_XTLV_HDR_SIZE_EX(opts
);
576 /* Init the datalength */
580 while (sbuflen
>= hdr_size
) {
581 ptlv
= (const bcm_xtlv_t
*)tlv_buf
;
582 bcm_xtlv_unpack_xtlv(ptlv
, &type
, &len
, &data
, opts
);
584 size
= bcm_xtlv_size_for_data(len
, opts
);
586 if (sbuflen
< 0) /* buffer overrun? */
603 bcm_xtlv_bcopy(const bcm_xtlv_t
*src
, bcm_xtlv_t
*dst
,
604 int src_buf_len
, int dst_buf_len
, bcm_xtlv_opts_t opts
)
606 bcm_xtlv_t
*dst_next
= NULL
;
607 src
= (src
&& bcm_valid_xtlv(src
, src_buf_len
, opts
)) ? src
: NULL
;
613 bcm_xtlv_unpack_xtlv(src
, &type
, &len
, &data
, opts
);
614 size
= bcm_xtlv_size_for_data(len
, opts
);
615 if (size
<= dst_buf_len
) {
616 bcm_xtlv_pack_xtlv(dst
, type
, len
, data
, opts
);
617 dst_next
= (bcm_xtlv_t
*)((uint8
*)dst
+ size
);