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