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