1 /*
2 * Driver O/S-independent utility routines
3 *
4 * Copyright (C) 1999-2017, Broadcom Corporation
5 *
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:
11 *
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.
19 *
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.
23 *
24 *
25 * <<Broadcom-WL-IPTag/Open:>>
26 *
27 * $Id: bcmxtlv.c 628611 2016-03-31 17:53:25Z $
28 */
29
30 #include <bcm_cfg.h>
31
32 #include <typedefs.h>
33 #include <bcmdefs.h>
34
35 #include <stdarg.h>
36
37 #ifdef BCMDRIVER
38 #include <osl.h>
39 #else /* !BCMDRIVER */
40 #include <stdlib.h> /* AS!!! */
41 #include <stdio.h>
42 #include <string.h>
43 #include <stdlib.h>
44 #ifndef ASSERT
45 #define ASSERT(exp)
46 #endif
MALLOCZ(void * o,size_t s)47 INLINE void* MALLOCZ(void *o, size_t s) { BCM_REFERENCE(o); return calloc(1, s); }
MFREE(void * o,void * p,size_t s)48 INLINE void MFREE(void *o, void *p, size_t s) { BCM_REFERENCE(o); BCM_REFERENCE(s); free(p); }
49 #endif /* !BCMDRIVER */
50
51 #include <bcmendian.h>
52 #include <bcmutils.h>
53
bcm_xtlv_size_for_data(int dlen,bcm_xtlv_opts_t opts)54 static INLINE int bcm_xtlv_size_for_data(int dlen, bcm_xtlv_opts_t opts)
55 {
56 return ((opts & BCM_XTLV_OPTION_ALIGN32) ? ALIGN_SIZE(dlen + BCM_XTLV_HDR_SIZE, 4)
57 : (dlen + BCM_XTLV_HDR_SIZE));
58 }
59
60 bcm_xtlv_t *
bcm_next_xtlv(bcm_xtlv_t * elt,int * buflen,bcm_xtlv_opts_t opts)61 bcm_next_xtlv(bcm_xtlv_t *elt, int *buflen, bcm_xtlv_opts_t opts)
62 {
63 int sz;
64 /* advance to next elt */
65 sz = BCM_XTLV_SIZE(elt, opts);
66 elt = (bcm_xtlv_t*)((uint8 *)elt + sz);
67 *buflen -= sz;
68
69 /* validate next elt */
70 if (!bcm_valid_xtlv(elt, *buflen, opts))
71 return NULL;
72
73 return elt;
74 }
75
76 int
bcm_xtlv_buf_init(bcm_xtlvbuf_t * tlv_buf,uint8 * buf,uint16 len,bcm_xtlv_opts_t opts)77 bcm_xtlv_buf_init(bcm_xtlvbuf_t *tlv_buf, uint8 *buf, uint16 len, bcm_xtlv_opts_t opts)
78 {
79 if (!tlv_buf || !buf || !len)
80 return BCME_BADARG;
81
82 tlv_buf->opts = opts;
83 tlv_buf->size = len;
84 tlv_buf->head = buf;
85 tlv_buf->buf = buf;
86 return BCME_OK;
87 }
88
89 uint16
bcm_xtlv_buf_len(bcm_xtlvbuf_t * tbuf)90 bcm_xtlv_buf_len(bcm_xtlvbuf_t *tbuf)
91 {
92 if (tbuf == NULL) return 0;
93 return (uint16)(tbuf->buf - tbuf->head);
94 }
95 uint16
bcm_xtlv_buf_rlen(bcm_xtlvbuf_t * tbuf)96 bcm_xtlv_buf_rlen(bcm_xtlvbuf_t *tbuf)
97 {
98 if (tbuf == NULL) return 0;
99 return tbuf->size - bcm_xtlv_buf_len(tbuf);
100 }
101 uint8 *
bcm_xtlv_buf(bcm_xtlvbuf_t * tbuf)102 bcm_xtlv_buf(bcm_xtlvbuf_t *tbuf)
103 {
104 if (tbuf == NULL) return NULL;
105 return tbuf->buf;
106 }
107 uint8 *
bcm_xtlv_head(bcm_xtlvbuf_t * tbuf)108 bcm_xtlv_head(bcm_xtlvbuf_t *tbuf)
109 {
110 if (tbuf == NULL) return NULL;
111 return tbuf->head;
112 }
113 int
bcm_xtlv_put_data(bcm_xtlvbuf_t * tbuf,uint16 type,const void * data,uint16 dlen)114 bcm_xtlv_put_data(bcm_xtlvbuf_t *tbuf, uint16 type, const void *data, uint16 dlen)
115 {
116 bcm_xtlv_t *xtlv;
117 int size;
118
119 if (tbuf == NULL)
120 return BCME_BADARG;
121 size = bcm_xtlv_size_for_data(dlen, tbuf->opts);
122 if (bcm_xtlv_buf_rlen(tbuf) < size)
123 return BCME_NOMEM;
124 xtlv = (bcm_xtlv_t *)bcm_xtlv_buf(tbuf);
125 xtlv->id = htol16(type);
126 xtlv->len = htol16(dlen);
127 memcpy(xtlv->data, data, dlen);
128 tbuf->buf += size;
129 return BCME_OK;
130 }
131 int
bcm_xtlv_put_8(bcm_xtlvbuf_t * tbuf,uint16 type,const int8 data)132 bcm_xtlv_put_8(bcm_xtlvbuf_t *tbuf, uint16 type, const int8 data)
133 {
134 bcm_xtlv_t *xtlv;
135 int size;
136
137 if (tbuf == NULL)
138 return BCME_BADARG;
139 size = bcm_xtlv_size_for_data(1, tbuf->opts);
140 if (bcm_xtlv_buf_rlen(tbuf) < size)
141 return BCME_NOMEM;
142 xtlv = (bcm_xtlv_t *)bcm_xtlv_buf(tbuf);
143 xtlv->id = htol16(type);
144 xtlv->len = htol16(sizeof(data));
145 xtlv->data[0] = data;
146 tbuf->buf += size;
147 return BCME_OK;
148 }
149 int
bcm_xtlv_put_16(bcm_xtlvbuf_t * tbuf,uint16 type,const int16 data)150 bcm_xtlv_put_16(bcm_xtlvbuf_t *tbuf, uint16 type, const int16 data)
151 {
152 bcm_xtlv_t *xtlv;
153 int size;
154
155 if (tbuf == NULL)
156 return BCME_BADARG;
157 size = bcm_xtlv_size_for_data(2, tbuf->opts);
158 if (bcm_xtlv_buf_rlen(tbuf) < size)
159 return BCME_NOMEM;
160
161 xtlv = (bcm_xtlv_t *)bcm_xtlv_buf(tbuf);
162 xtlv->id = htol16(type);
163 xtlv->len = htol16(sizeof(data));
164 htol16_ua_store(data, xtlv->data);
165 tbuf->buf += size;
166 return BCME_OK;
167 }
168 int
bcm_xtlv_put_32(bcm_xtlvbuf_t * tbuf,uint16 type,const int32 data)169 bcm_xtlv_put_32(bcm_xtlvbuf_t *tbuf, uint16 type, const int32 data)
170 {
171 bcm_xtlv_t *xtlv;
172 int size;
173
174 if (tbuf == NULL)
175 return BCME_BADARG;
176 size = bcm_xtlv_size_for_data(4, tbuf->opts);
177 if (bcm_xtlv_buf_rlen(tbuf) < size)
178 return BCME_NOMEM;
179 xtlv = (bcm_xtlv_t *)bcm_xtlv_buf(tbuf);
180 xtlv->id = htol16(type);
181 xtlv->len = htol16(sizeof(data));
182 htol32_ua_store(data, xtlv->data);
183 tbuf->buf += size;
184 return BCME_OK;
185 }
186
187 /*
188 * upacks xtlv record from buf checks the type
189 * copies data to callers buffer
190 * advances tlv pointer to next record
191 * caller's resposible for dst space check
192 */
193 int
bcm_unpack_xtlv_entry(uint8 ** tlv_buf,uint16 xpct_type,uint16 xpct_len,void * dst,bcm_xtlv_opts_t opts)194 bcm_unpack_xtlv_entry(uint8 **tlv_buf, uint16 xpct_type, uint16 xpct_len, void *dst,
195 bcm_xtlv_opts_t opts)
196 {
197 bcm_xtlv_t *ptlv = (bcm_xtlv_t *)*tlv_buf;
198 uint16 len;
199 uint16 type;
200
201 ASSERT(ptlv);
202 /* tlv headr is always packed in LE order */
203 len = ltoh16(ptlv->len);
204 type = ltoh16(ptlv->id);
205 if (len == 0) {
206 /* z-len tlv headers: allow, but don't process */
207 printf("z-len, skip unpack\n");
208 } else {
209 if ((type != xpct_type) ||
210 (len > xpct_len)) {
211 printf("xtlv_unpack Error: found[type:%d,len:%d] != xpct[type:%d,len:%d]\n",
212 type, len, xpct_type, xpct_len);
213 return BCME_BADARG;
214 }
215 /* copy tlv record to caller's buffer */
216 memcpy(dst, ptlv->data, ptlv->len);
217 }
218 *tlv_buf = (uint8*)(*tlv_buf) + BCM_XTLV_SIZE(ptlv, opts);
219 return BCME_OK;
220 }
221
222 /*
223 * packs user data into tlv record
224 * advances tlv pointer to next xtlv slot
225 * buflen is used for tlv_buf space check
226 */
227 int
bcm_pack_xtlv_entry(uint8 ** tlv_buf,uint16 * buflen,uint16 type,uint16 len,void * src,bcm_xtlv_opts_t opts)228 bcm_pack_xtlv_entry(uint8 **tlv_buf, uint16 *buflen, uint16 type, uint16 len, void *src,
229 bcm_xtlv_opts_t opts)
230 {
231 bcm_xtlv_t *ptlv = (bcm_xtlv_t *)*tlv_buf;
232 int size;
233
234 ASSERT(ptlv);
235 ASSERT(src);
236
237 size = bcm_xtlv_size_for_data(len, opts);
238
239 /* copy data from tlv buffer to dst provided by user */
240 if (size > *buflen) {
241 printf("bcm_pack_xtlv_entry: no space tlv_buf: requested:%d, available:%d\n",
242 size, *buflen);
243 return BCME_BADLEN;
244 }
245 ptlv->id = htol16(type);
246 ptlv->len = htol16(len);
247
248 /* copy callers data */
249 memcpy(ptlv->data, src, len);
250
251 /* advance callers pointer to tlv buff */
252 *tlv_buf = (uint8*)(*tlv_buf) + size;
253 /* decrement the len */
254 *buflen -= (uint16)size;
255 return BCME_OK;
256 }
257
258 /*
259 * unpack all xtlv records from the issue a callback
260 * to set function one call per found tlv record
261 */
262 int
bcm_unpack_xtlv_buf(void * ctx,uint8 * tlv_buf,uint16 buflen,bcm_xtlv_opts_t opts,bcm_xtlv_unpack_cbfn_t * cbfn)263 bcm_unpack_xtlv_buf(void *ctx, uint8 *tlv_buf, uint16 buflen, bcm_xtlv_opts_t opts,
264 bcm_xtlv_unpack_cbfn_t *cbfn)
265 {
266 uint16 len;
267 uint16 type;
268 int res = BCME_OK;
269 int size;
270 bcm_xtlv_t *ptlv;
271 int sbuflen = buflen;
272
273 ASSERT(!buflen || tlv_buf);
274 ASSERT(!buflen || cbfn);
275
276 while (sbuflen >= (int)BCM_XTLV_HDR_SIZE) {
277 ptlv = (bcm_xtlv_t *)tlv_buf;
278
279 /* tlv header is always packed in LE order */
280 len = ltoh16(ptlv->len);
281 type = ltoh16(ptlv->id);
282
283 size = bcm_xtlv_size_for_data(len, opts);
284
285 sbuflen -= size;
286 /* check for possible buffer overrun */
287 if (sbuflen < 0)
288 break;
289
290 if ((res = cbfn(ctx, ptlv->data, type, len)) != BCME_OK)
291 break;
292 tlv_buf = (uint8*)tlv_buf + size;
293 }
294 return res;
295 }
296
297 int
bcm_pack_xtlv_buf(void * ctx,void * 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)298 bcm_pack_xtlv_buf(void *ctx, void *tlv_buf, uint16 buflen, bcm_xtlv_opts_t opts,
299 bcm_pack_xtlv_next_info_cbfn_t get_next, bcm_pack_xtlv_pack_next_cbfn_t pack_next,
300 int *outlen)
301 {
302 int res = BCME_OK;
303 uint16 tlv_id;
304 uint16 tlv_len;
305 uint8 *startp;
306 uint8 *endp;
307 uint8 *buf;
308 bool more;
309 int size;
310
311 ASSERT(get_next && pack_next);
312
313 buf = (uint8 *)tlv_buf;
314 startp = buf;
315 endp = (uint8 *)buf + buflen;
316 more = TRUE;
317 while (more && (buf < endp)) {
318 more = get_next(ctx, &tlv_id, &tlv_len);
319 size = bcm_xtlv_size_for_data(tlv_len, opts);
320 if ((buf + size) > endp) {
321 res = BCME_BUFTOOSHORT;
322 goto done;
323 }
324
325 htol16_ua_store(tlv_id, buf);
326 htol16_ua_store(tlv_len, buf + sizeof(tlv_id));
327 pack_next(ctx, tlv_id, tlv_len, buf + BCM_XTLV_HDR_SIZE);
328 buf += size;
329 }
330
331 if (more)
332 res = BCME_BUFTOOSHORT;
333
334 done:
335 if (outlen) {
336 *outlen = (int)(buf - startp);
337 }
338 return res;
339 }
340
341 /*
342 * pack xtlv buffer from memory according to xtlv_desc_t
343 */
344 int
bcm_pack_xtlv_buf_from_mem(void ** tlv_buf,uint16 * buflen,xtlv_desc_t * items,bcm_xtlv_opts_t opts)345 bcm_pack_xtlv_buf_from_mem(void **tlv_buf, uint16 *buflen, xtlv_desc_t *items,
346 bcm_xtlv_opts_t opts)
347 {
348 int res = BCME_OK;
349 uint8 *ptlv = (uint8 *)*tlv_buf;
350
351 while (items->type != 0) {
352 if ((items->len > 0) && (res = bcm_pack_xtlv_entry(&ptlv,
353 buflen, items->type,
354 items->len, items->ptr, opts) != BCME_OK)) {
355 break;
356 }
357 items++;
358 }
359 *tlv_buf = ptlv; /* update the external pointer */
360 return res;
361 }
362
363 /*
364 * unpack xtlv buffer to memory according to xtlv_desc_t
365 *
366 */
367 int
bcm_unpack_xtlv_buf_to_mem(void * tlv_buf,int * buflen,xtlv_desc_t * items,bcm_xtlv_opts_t opts)368 bcm_unpack_xtlv_buf_to_mem(void *tlv_buf, int *buflen, xtlv_desc_t *items, bcm_xtlv_opts_t opts)
369 {
370 int res = BCME_OK;
371 bcm_xtlv_t *elt;
372
373 elt = bcm_valid_xtlv((bcm_xtlv_t *)tlv_buf, *buflen, opts) ? (bcm_xtlv_t *)tlv_buf : NULL;
374 if (!elt || !items) {
375 res = BCME_BADARG;
376 return res;
377 }
378
379 for (; elt != NULL && res == BCME_OK; elt = bcm_next_xtlv(elt, buflen, opts)) {
380 /* find matches in desc_t items */
381 xtlv_desc_t *dst_desc = items;
382 uint16 len = ltoh16(elt->len);
383
384 while (dst_desc->type != 0) {
385 if (ltoh16(elt->id) == dst_desc->type) {
386 if (len != dst_desc->len) {
387 res = BCME_BADLEN;
388 } else {
389 memcpy(dst_desc->ptr, elt->data, len);
390 }
391 break;
392 }
393 dst_desc++;
394 }
395 }
396
397 if (res == BCME_OK && *buflen != 0)
398 res = BCME_BUFTOOSHORT;
399
400 return res;
401 }
402
403 /*
404 * return data pointer of a given ID from xtlv buffer.
405 * If the specified xTLV ID is found, on return *data_len_out will contain
406 * the the data length of the xTLV ID.
407 */
408 void *
bcm_get_data_from_xtlv_buf(uint8 * tlv_buf,uint16 buflen,uint16 id,uint16 * datalen_out,bcm_xtlv_opts_t opts)409 bcm_get_data_from_xtlv_buf(uint8 *tlv_buf, uint16 buflen, uint16 id,
410 uint16 *datalen_out, bcm_xtlv_opts_t opts)
411 {
412 void *retptr = NULL;
413 uint16 type, len;
414 int size;
415 bcm_xtlv_t *ptlv;
416 int sbuflen = buflen;
417
418 while (sbuflen >= (int)BCM_XTLV_HDR_SIZE) {
419 ptlv = (bcm_xtlv_t *)tlv_buf;
420
421 /* tlv header is always packed in LE order */
422 type = ltoh16(ptlv->id);
423 len = ltoh16(ptlv->len);
424 size = bcm_xtlv_size_for_data(len, opts);
425
426 sbuflen -= size;
427 /* check for possible buffer overrun */
428 if (sbuflen < 0) {
429 printf("%s %d: Invalid sbuflen %d\n",
430 __FUNCTION__, __LINE__, sbuflen);
431 break;
432 }
433
434 if (id == type) {
435 retptr = ptlv->data;
436 if (datalen_out) {
437 *datalen_out = len;
438 }
439 break;
440 }
441 tlv_buf += size;
442 }
443
444 return retptr;
445 }
446
bcm_xtlv_size(const bcm_xtlv_t * elt,bcm_xtlv_opts_t opts)447 int bcm_xtlv_size(const bcm_xtlv_t *elt, bcm_xtlv_opts_t opts)
448 {
449 int size; /* entire size of the XTLV including header, data, and optional padding */
450 int len; /* XTLV's value real length wthout padding */
451
452 len = BCM_XTLV_LEN(elt);
453
454 size = bcm_xtlv_size_for_data(len, opts);
455
456 return size;
457 }
458