123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624 |
- /*
- * Driver O/S-independent utility routines
- *
- * Portions of this code are copyright (c) 2020 Cypress Semiconductor Corporation
- *
- * Copyright (C) 1999-2020, Broadcom Corporation
- *
- * Unless you and Broadcom execute a separate written software license
- * agreement governing use of this software, this software is licensed to you
- * under the terms of the GNU General Public License version 2 (the "GPL"),
- * available at http://www.broadcom.com/licenses/GPLv2.php, with the
- * following added to such license:
- *
- * As a special exception, the copyright holders of this software give you
- * permission to link this software with independent modules, and to copy and
- * distribute the resulting executable under terms of your choice, provided that
- * you also meet, for each linked independent module, the terms and conditions of
- * the license of that module. An independent module is a module which is not
- * derived from this software. The special exception does not apply to any
- * modifications of the software.
- *
- * Notwithstanding the above, under no circumstances may you combine this
- * software in any way with any other Broadcom software provided under a license
- * other than the GPL, without Broadcom's express prior written consent.
- *
- *
- * <<Broadcom-WL-IPTag/Open:>>
- *
- * $Id: bcmxtlv.c 700655 2017-05-20 06:09:06Z $
- */
- #include <bcm_cfg.h>
- #include <typedefs.h>
- #include <bcmdefs.h>
- #include <stdarg.h>
- #ifdef BCMDRIVER
- #include <osl.h>
- #else /* !BCMDRIVER */
- #include <stdio.h>
- #include <string.h>
- #include <stdlib.h>
- #ifndef ASSERT
- #define ASSERT(exp)
- #endif // endif
- #endif /* !BCMDRIVER */
- #include <bcmtlv.h>
- #include <bcmendian.h>
- #include <bcmutils.h>
- int
- bcm_xtlv_hdr_size(bcm_xtlv_opts_t opts)
- {
- int len = (int)OFFSETOF(bcm_xtlv_t, data); /* nominal */
- if (opts & BCM_XTLV_OPTION_LENU8) --len;
- if (opts & BCM_XTLV_OPTION_IDU8) --len;
- return len;
- }
- bool
- bcm_valid_xtlv(const bcm_xtlv_t *elt, int buf_len, bcm_xtlv_opts_t opts)
- {
- return elt != NULL &&
- buf_len >= bcm_xtlv_hdr_size(opts) &&
- buf_len >= bcm_xtlv_size(elt, opts);
- }
- int
- bcm_xtlv_size_for_data(int dlen, bcm_xtlv_opts_t opts)
- {
- int hsz;
- hsz = bcm_xtlv_hdr_size(opts);
- return ((opts & BCM_XTLV_OPTION_ALIGN32) ? ALIGN_SIZE(dlen + hsz, 4)
- : (dlen + hsz));
- }
- int
- bcm_xtlv_size(const bcm_xtlv_t *elt, bcm_xtlv_opts_t opts)
- {
- int size; /* size including header, data, and any pad */
- int len; /* length wthout padding */
- len = BCM_XTLV_LEN_EX(elt, opts);
- size = bcm_xtlv_size_for_data(len, opts);
- return size;
- }
- int
- bcm_xtlv_len(const bcm_xtlv_t *elt, bcm_xtlv_opts_t opts)
- {
- const uint8 *lenp;
- int len;
- lenp = (const uint8 *)&elt->len; /* nominal */
- if (opts & BCM_XTLV_OPTION_IDU8) {
- --lenp;
- }
- if (opts & BCM_XTLV_OPTION_LENU8) {
- len = *lenp;
- } else if (opts & BCM_XTLV_OPTION_LENBE) {
- len = (uint32)hton16(elt->len);
- } else {
- len = ltoh16_ua(lenp);
- }
- return len;
- }
- int
- bcm_xtlv_id(const bcm_xtlv_t *elt, bcm_xtlv_opts_t opts)
- {
- int id = 0;
- if (opts & BCM_XTLV_OPTION_IDU8) {
- id = *(const uint8 *)elt;
- } else if (opts & BCM_XTLV_OPTION_IDBE) {
- id = (uint32)hton16(elt->id);
- } else {
- id = ltoh16_ua((const uint8 *)elt);
- }
- return id;
- }
- bcm_xtlv_t *
- bcm_next_xtlv(const bcm_xtlv_t *elt, int *buflen, bcm_xtlv_opts_t opts)
- {
- int sz;
- /* advance to next elt */
- sz = BCM_XTLV_SIZE_EX(elt, opts);
- elt = (const bcm_xtlv_t*)((const uint8 *)elt + sz);
- *buflen -= sz;
- /* validate next elt */
- if (!bcm_valid_xtlv(elt, *buflen, opts))
- return NULL;
- GCC_DIAGNOSTIC_PUSH_SUPPRESS_CAST();
- return (bcm_xtlv_t *)(elt);
- GCC_DIAGNOSTIC_POP();
- }
- int
- bcm_xtlv_buf_init(bcm_xtlvbuf_t *tlv_buf, uint8 *buf, uint16 len, bcm_xtlv_opts_t opts)
- {
- if (!tlv_buf || !buf || !len)
- return BCME_BADARG;
- tlv_buf->opts = opts;
- tlv_buf->size = len;
- tlv_buf->head = buf;
- tlv_buf->buf = buf;
- return BCME_OK;
- }
- uint16
- bcm_xtlv_buf_len(bcm_xtlvbuf_t *tbuf)
- {
- uint16 len;
- if (tbuf)
- len = (uint16)(tbuf->buf - tbuf->head);
- else
- len = 0;
- return len;
- }
- uint16
- bcm_xtlv_buf_rlen(bcm_xtlvbuf_t *tbuf)
- {
- uint16 rlen;
- if (tbuf)
- rlen = tbuf->size - bcm_xtlv_buf_len(tbuf);
- else
- rlen = 0;
- return rlen;
- }
- uint8 *
- bcm_xtlv_buf(bcm_xtlvbuf_t *tbuf)
- {
- return tbuf ? tbuf->buf : NULL;
- }
- uint8 *
- bcm_xtlv_head(bcm_xtlvbuf_t *tbuf)
- {
- return tbuf ? tbuf->head : NULL;
- }
- void
- bcm_xtlv_pack_xtlv(bcm_xtlv_t *xtlv, uint16 type, uint16 len, const uint8 *data,
- bcm_xtlv_opts_t opts)
- {
- uint8 *data_buf;
- bcm_xtlv_opts_t mask = BCM_XTLV_OPTION_IDU8 | BCM_XTLV_OPTION_LENU8;
- if (!(opts & mask)) { /* default */
- uint8 *idp = (uint8 *)xtlv;
- uint8 *lenp = idp + sizeof(xtlv->id);
- htol16_ua_store(type, idp);
- htol16_ua_store(len, lenp);
- data_buf = lenp + sizeof(uint16);
- } else if ((opts & mask) == mask) { /* u8 id and u8 len */
- uint8 *idp = (uint8 *)xtlv;
- uint8 *lenp = idp + 1;
- *idp = (uint8)type;
- *lenp = (uint8)len;
- data_buf = lenp + sizeof(uint8);
- } else if (opts & BCM_XTLV_OPTION_IDU8) { /* u8 id, u16 len */
- uint8 *idp = (uint8 *)xtlv;
- uint8 *lenp = idp + 1;
- *idp = (uint8)type;
- htol16_ua_store(len, lenp);
- data_buf = lenp + sizeof(uint16);
- } else if (opts & BCM_XTLV_OPTION_LENU8) { /* u16 id, u8 len */
- uint8 *idp = (uint8 *)xtlv;
- uint8 *lenp = idp + sizeof(uint16);
- htol16_ua_store(type, idp);
- *lenp = (uint8)len;
- data_buf = lenp + sizeof(uint8);
- } else {
- bool Unexpected_xtlv_option = TRUE;
- BCM_REFERENCE(Unexpected_xtlv_option);
- ASSERT(!Unexpected_xtlv_option);
- return;
- }
- if (opts & BCM_XTLV_OPTION_LENU8) {
- ASSERT(len <= 0x00ff);
- len &= 0xff;
- }
- if (data != NULL)
- memcpy(data_buf, data, len);
- }
- /* xtlv header is always packed in LE order */
- void
- bcm_xtlv_unpack_xtlv(const bcm_xtlv_t *xtlv, uint16 *type, uint16 *len,
- const uint8 **data, bcm_xtlv_opts_t opts)
- {
- if (type)
- *type = (uint16)bcm_xtlv_id(xtlv, opts);
- if (len)
- *len = (uint16)bcm_xtlv_len(xtlv, opts);
- if (data)
- *data = (const uint8 *)xtlv + BCM_XTLV_HDR_SIZE_EX(opts);
- }
- int
- bcm_xtlv_put_data(bcm_xtlvbuf_t *tbuf, uint16 type, const uint8 *data, int n)
- {
- bcm_xtlv_t *xtlv;
- int size;
- if (tbuf == NULL)
- return BCME_BADARG;
- size = bcm_xtlv_size_for_data(n, tbuf->opts);
- if (bcm_xtlv_buf_rlen(tbuf) < size)
- return BCME_NOMEM;
- xtlv = (bcm_xtlv_t *)bcm_xtlv_buf(tbuf);
- bcm_xtlv_pack_xtlv(xtlv, type, (uint16)n, data, tbuf->opts);
- tbuf->buf += size; /* note: data may be NULL, reserves space */
- return BCME_OK;
- }
- static int
- bcm_xtlv_put_int(bcm_xtlvbuf_t *tbuf, uint16 type, const uint8 *data, int n, int int_sz)
- {
- bcm_xtlv_t *xtlv;
- int xtlv_len;
- uint8 *xtlv_data;
- int err = BCME_OK;
- if (tbuf == NULL) {
- err = BCME_BADARG;
- goto done;
- }
- xtlv = (bcm_xtlv_t *)bcm_xtlv_buf(tbuf);
- /* put type and length in xtlv and reserve data space */
- xtlv_len = n * int_sz;
- err = bcm_xtlv_put_data(tbuf, type, NULL, xtlv_len);
- if (err != BCME_OK)
- goto done;
- xtlv_data = (uint8 *)xtlv + bcm_xtlv_hdr_size(tbuf->opts);
- /* write data w/ little-endianness into buffer - single loop, aligned access */
- for (; n != 0; --n, xtlv_data += int_sz, data += int_sz) {
- switch (int_sz) {
- case sizeof(uint8):
- break;
- case sizeof(uint16):
- {
- uint16 v = load16_ua(data);
- htol16_ua_store(v, xtlv_data);
- break;
- }
- case sizeof(uint32):
- {
- uint32 v = load32_ua(data);
- htol32_ua_store(v, xtlv_data);
- break;
- }
- case sizeof(uint64):
- {
- uint64 v = load64_ua(data);
- htol64_ua_store(v, xtlv_data);
- break;
- }
- default:
- err = BCME_UNSUPPORTED;
- goto done;
- }
- }
- done:
- return err;
- }
- int
- bcm_xtlv_put16(bcm_xtlvbuf_t *tbuf, uint16 type, const uint16 *data, int n)
- {
- return bcm_xtlv_put_int(tbuf, type, (const uint8 *)data, n, sizeof(uint16));
- }
- int
- bcm_xtlv_put32(bcm_xtlvbuf_t *tbuf, uint16 type, const uint32 *data, int n)
- {
- return bcm_xtlv_put_int(tbuf, type, (const uint8 *)data, n, sizeof(uint32));
- }
- int
- bcm_xtlv_put64(bcm_xtlvbuf_t *tbuf, uint16 type, const uint64 *data, int n)
- {
- return bcm_xtlv_put_int(tbuf, type, (const uint8 *)data, n, sizeof(uint64));
- }
- /*
- * upacks xtlv record from buf checks the type
- * copies data to callers buffer
- * advances tlv pointer to next record
- * caller's resposible for dst space check
- */
- int
- bcm_unpack_xtlv_entry(const uint8 **tlv_buf, uint16 xpct_type, uint16 xpct_len,
- uint8 *dst_data, bcm_xtlv_opts_t opts)
- {
- const bcm_xtlv_t *ptlv = (const bcm_xtlv_t *)*tlv_buf;
- uint16 len;
- uint16 type;
- const uint8 *data;
- ASSERT(ptlv);
- bcm_xtlv_unpack_xtlv(ptlv, &type, &len, &data, opts);
- if (len) {
- if ((type != xpct_type) || (len > xpct_len))
- return BCME_BADARG;
- if (dst_data && data)
- memcpy(dst_data, data, len); /* copy data to dst */
- }
- *tlv_buf += BCM_XTLV_SIZE_EX(ptlv, opts);
- return BCME_OK;
- }
- /*
- * packs user data into tlv record and advances tlv pointer to next xtlv slot
- * buflen is used for tlv_buf space check
- */
- int
- bcm_pack_xtlv_entry(uint8 **tlv_buf, uint16 *buflen, uint16 type, uint16 len,
- const uint8 *src_data, bcm_xtlv_opts_t opts)
- {
- bcm_xtlv_t *ptlv = (bcm_xtlv_t *)*tlv_buf;
- int size;
- ASSERT(ptlv);
- size = bcm_xtlv_size_for_data(len, opts);
- /* copy data from tlv buffer to dst provided by user */
- if (size > *buflen)
- return BCME_BADLEN;
- bcm_xtlv_pack_xtlv(ptlv, type, len, src_data, opts);
- /* advance callers pointer to tlv buff */
- *tlv_buf = (uint8*)(*tlv_buf) + size;
- /* decrement the len */
- *buflen -= (uint16)size;
- return BCME_OK;
- }
- /*
- * unpack all xtlv records from the issue a callback
- * to set function one call per found tlv record
- */
- int
- bcm_unpack_xtlv_buf(void *ctx, const uint8 *tlv_buf, uint16 buflen, bcm_xtlv_opts_t opts,
- bcm_xtlv_unpack_cbfn_t *cbfn)
- {
- uint16 len;
- uint16 type;
- int res = BCME_OK;
- int size;
- const bcm_xtlv_t *ptlv;
- int sbuflen = buflen;
- const uint8 *data;
- int hdr_size;
- ASSERT(!buflen || tlv_buf);
- ASSERT(!buflen || cbfn);
- hdr_size = BCM_XTLV_HDR_SIZE_EX(opts);
- while (sbuflen >= hdr_size) {
- ptlv = (const bcm_xtlv_t *)tlv_buf;
- bcm_xtlv_unpack_xtlv(ptlv, &type, &len, &data, opts);
- size = bcm_xtlv_size_for_data(len, opts);
- sbuflen -= size;
- if (sbuflen < 0) /* check for buffer overrun */
- break;
- if ((res = cbfn(ctx, data, type, len)) != BCME_OK)
- break;
- tlv_buf += size;
- }
- return res;
- }
- 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)
- {
- int res = BCME_OK;
- uint16 tlv_id;
- uint16 tlv_len;
- uint8 *startp;
- uint8 *endp;
- uint8 *buf;
- bool more;
- int size;
- int hdr_size;
- ASSERT(get_next && pack_next);
- buf = tlv_buf;
- startp = buf;
- endp = (uint8 *)buf + buflen;
- more = TRUE;
- hdr_size = BCM_XTLV_HDR_SIZE_EX(opts);
- while (more && (buf < endp)) {
- more = get_next(ctx, &tlv_id, &tlv_len);
- size = bcm_xtlv_size_for_data(tlv_len, opts);
- if ((buf + size) > endp) {
- res = BCME_BUFTOOSHORT;
- goto done;
- }
- bcm_xtlv_pack_xtlv((bcm_xtlv_t *)buf, tlv_id, tlv_len, NULL, opts);
- pack_next(ctx, tlv_id, tlv_len, buf + hdr_size);
- buf += size;
- }
- if (more)
- res = BCME_BUFTOOSHORT;
- done:
- if (outlen) {
- *outlen = (int)(buf - startp);
- }
- return res;
- }
- /*
- * pack xtlv buffer from memory according to xtlv_desc_t
- */
- int
- bcm_pack_xtlv_buf_from_mem(uint8 **tlv_buf, uint16 *buflen, const xtlv_desc_t *items,
- bcm_xtlv_opts_t opts)
- {
- int res = BCME_OK;
- uint8 *ptlv = *tlv_buf;
- while (items->type != 0) {
- if (items->len && items->ptr) {
- res = bcm_pack_xtlv_entry(&ptlv, buflen, items->type,
- items->len, items->ptr, opts);
- if (res != BCME_OK)
- break;
- }
- items++;
- }
- *tlv_buf = ptlv; /* update the external pointer */
- return res;
- }
- /*
- * unpack xtlv buffer to memory according to xtlv_desc_t
- *
- */
- int
- bcm_unpack_xtlv_buf_to_mem(uint8 *tlv_buf, int *buflen, xtlv_desc_t *items,
- bcm_xtlv_opts_t opts)
- {
- int res = BCME_OK;
- bcm_xtlv_t *elt;
- elt = bcm_valid_xtlv((bcm_xtlv_t *)tlv_buf, *buflen, opts) ? (bcm_xtlv_t *)tlv_buf : NULL;
- if (!elt || !items) {
- res = BCME_BADARG;
- return res;
- }
- for (; elt != NULL && res == BCME_OK; elt = bcm_next_xtlv(elt, buflen, opts)) {
- /* find matches in desc_t items */
- xtlv_desc_t *dst_desc = items;
- uint16 len, type;
- const uint8 *data;
- bcm_xtlv_unpack_xtlv(elt, &type, &len, &data, opts);
- while (dst_desc->type != 0) {
- if (type == dst_desc->type) {
- if (len != dst_desc->len) {
- res = BCME_BADLEN;
- } else {
- memcpy(dst_desc->ptr, data, len);
- }
- break;
- }
- dst_desc++;
- }
- }
- if (res == BCME_OK && *buflen != 0)
- res = BCME_BUFTOOSHORT;
- return res;
- }
- /*
- * return data pointer of a given ID from xtlv buffer.
- * If the specified xTLV ID is found, on return *datalen will contain
- * the the data length of the xTLV ID.
- */
- const uint8*
- bcm_get_data_from_xtlv_buf(const uint8 *tlv_buf, uint16 buflen, uint16 id,
- uint16 *datalen, bcm_xtlv_opts_t opts)
- {
- const uint8 *retptr = NULL;
- uint16 type, len;
- int size;
- const bcm_xtlv_t *ptlv;
- int sbuflen = buflen;
- const uint8 *data;
- int hdr_size;
- hdr_size = BCM_XTLV_HDR_SIZE_EX(opts);
- /* Init the datalength */
- if (datalen) {
- *datalen = 0;
- }
- while (sbuflen >= hdr_size) {
- ptlv = (const bcm_xtlv_t *)tlv_buf;
- bcm_xtlv_unpack_xtlv(ptlv, &type, &len, &data, opts);
- size = bcm_xtlv_size_for_data(len, opts);
- sbuflen -= size;
- if (sbuflen < 0) /* buffer overrun? */
- break;
- if (id == type) {
- retptr = data;
- if (datalen)
- *datalen = len;
- break;
- }
- tlv_buf += size;
- }
- return retptr;
- }
- 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)
- {
- bcm_xtlv_t *dst_next = NULL;
- src = (src && bcm_valid_xtlv(src, src_buf_len, opts)) ? src : NULL;
- if (src && dst) {
- uint16 type;
- uint16 len;
- const uint8 *data;
- int size;
- bcm_xtlv_unpack_xtlv(src, &type, &len, &data, opts);
- size = bcm_xtlv_size_for_data(len, opts);
- if (size <= dst_buf_len) {
- bcm_xtlv_pack_xtlv(dst, type, len, data, opts);
- dst_next = (bcm_xtlv_t *)((uint8 *)dst + size);
- }
- }
- return dst_next;
- }
|