summaryrefslogtreecommitdiff
path: root/drivers/net/wireless/bcmdhd_1363/bcmxtlv.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/wireless/bcmdhd_1363/bcmxtlv.c')
-rw-r--r--drivers/net/wireless/bcmdhd_1363/bcmxtlv.c457
1 files changed, 457 insertions, 0 deletions
diff --git a/drivers/net/wireless/bcmdhd_1363/bcmxtlv.c b/drivers/net/wireless/bcmdhd_1363/bcmxtlv.c
new file mode 100644
index 000000000000..a9213b7319be
--- /dev/null
+++ b/drivers/net/wireless/bcmdhd_1363/bcmxtlv.c
@@ -0,0 +1,457 @@
+/*
+ * Driver O/S-independent utility routines
+ *
+ * Copyright (C) 1999-2017, 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 527361 2015-01-17 01:48:34Z $
+ */
+
+#include <bcm_cfg.h>
+
+#include <typedefs.h>
+#include <bcmdefs.h>
+
+#include <stdarg.h>
+
+#ifdef BCMDRIVER
+#include <osl.h>
+#else /* !BCMDRIVER */
+ #include <stdlib.h> /* AS!!! */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#ifndef ASSERT
+#define ASSERT(exp)
+#endif
+INLINE void* MALLOCZ(void *o, size_t s) { BCM_REFERENCE(o); return calloc(1, s); }
+INLINE void MFREE(void *o, void *p, size_t s) { BCM_REFERENCE(o); BCM_REFERENCE(s); free(p); }
+#endif /* !BCMDRIVER */
+
+#include <bcmendian.h>
+#include <bcmutils.h>
+
+static INLINE int bcm_xtlv_size_for_data(int dlen, bcm_xtlv_opts_t opts)
+{
+ return ((opts & BCM_XTLV_OPTION_ALIGN32) ? ALIGN_SIZE(dlen + BCM_XTLV_HDR_SIZE, 4)
+ : (dlen + BCM_XTLV_HDR_SIZE));
+}
+
+bcm_xtlv_t *
+bcm_next_xtlv(bcm_xtlv_t *elt, int *buflen, bcm_xtlv_opts_t opts)
+{
+ int sz;
+ /* advance to next elt */
+ sz = BCM_XTLV_SIZE(elt, opts);
+ elt = (bcm_xtlv_t*)((uint8 *)elt + sz);
+ *buflen -= sz;
+
+ /* validate next elt */
+ if (!bcm_valid_xtlv(elt, *buflen, opts))
+ return NULL;
+
+ return elt;
+}
+
+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)
+{
+ if (tbuf == NULL) return 0;
+ return (uint16)(tbuf->buf - tbuf->head);
+}
+uint16
+bcm_xtlv_buf_rlen(bcm_xtlvbuf_t *tbuf)
+{
+ if (tbuf == NULL) return 0;
+ return tbuf->size - bcm_xtlv_buf_len(tbuf);
+}
+uint8 *
+bcm_xtlv_buf(bcm_xtlvbuf_t *tbuf)
+{
+ if (tbuf == NULL) return NULL;
+ return tbuf->buf;
+}
+uint8 *
+bcm_xtlv_head(bcm_xtlvbuf_t *tbuf)
+{
+ if (tbuf == NULL) return NULL;
+ return tbuf->head;
+}
+int
+bcm_xtlv_put_data(bcm_xtlvbuf_t *tbuf, uint16 type, const void *data, uint16 dlen)
+{
+ bcm_xtlv_t *xtlv;
+ int size;
+
+ if (tbuf == NULL)
+ return BCME_BADARG;
+ size = bcm_xtlv_size_for_data(dlen, tbuf->opts);
+ if (bcm_xtlv_buf_rlen(tbuf) < size)
+ return BCME_NOMEM;
+ xtlv = (bcm_xtlv_t *)bcm_xtlv_buf(tbuf);
+ xtlv->id = htol16(type);
+ xtlv->len = htol16(dlen);
+ memcpy(xtlv->data, data, dlen);
+ tbuf->buf += size;
+ return BCME_OK;
+}
+int
+bcm_xtlv_put_8(bcm_xtlvbuf_t *tbuf, uint16 type, const int8 data)
+{
+ bcm_xtlv_t *xtlv;
+ int size;
+
+ if (tbuf == NULL)
+ return BCME_BADARG;
+ size = bcm_xtlv_size_for_data(1, tbuf->opts);
+ if (bcm_xtlv_buf_rlen(tbuf) < size)
+ return BCME_NOMEM;
+ xtlv = (bcm_xtlv_t *)bcm_xtlv_buf(tbuf);
+ xtlv->id = htol16(type);
+ xtlv->len = htol16(sizeof(data));
+ xtlv->data[0] = data;
+ tbuf->buf += size;
+ return BCME_OK;
+}
+int
+bcm_xtlv_put_16(bcm_xtlvbuf_t *tbuf, uint16 type, const int16 data)
+{
+ bcm_xtlv_t *xtlv;
+ int size;
+
+ if (tbuf == NULL)
+ return BCME_BADARG;
+ size = bcm_xtlv_size_for_data(2, tbuf->opts);
+ if (bcm_xtlv_buf_rlen(tbuf) < size)
+ return BCME_NOMEM;
+
+ xtlv = (bcm_xtlv_t *)bcm_xtlv_buf(tbuf);
+ xtlv->id = htol16(type);
+ xtlv->len = htol16(sizeof(data));
+ htol16_ua_store(data, xtlv->data);
+ tbuf->buf += size;
+ return BCME_OK;
+}
+int
+bcm_xtlv_put_32(bcm_xtlvbuf_t *tbuf, uint16 type, const int32 data)
+{
+ bcm_xtlv_t *xtlv;
+ int size;
+
+ if (tbuf == NULL)
+ return BCME_BADARG;
+ size = bcm_xtlv_size_for_data(4, tbuf->opts);
+ if (bcm_xtlv_buf_rlen(tbuf) < size)
+ return BCME_NOMEM;
+ xtlv = (bcm_xtlv_t *)bcm_xtlv_buf(tbuf);
+ xtlv->id = htol16(type);
+ xtlv->len = htol16(sizeof(data));
+ htol32_ua_store(data, xtlv->data);
+ tbuf->buf += size;
+ return BCME_OK;
+}
+
+/*
+ * 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(uint8 **tlv_buf, uint16 xpct_type, uint16 xpct_len, void *dst,
+ bcm_xtlv_opts_t opts)
+{
+ bcm_xtlv_t *ptlv = (bcm_xtlv_t *)*tlv_buf;
+ uint16 len;
+ uint16 type;
+
+ ASSERT(ptlv);
+ /* tlv headr is always packed in LE order */
+ len = ltoh16(ptlv->len);
+ type = ltoh16(ptlv->id);
+ if (len == 0) {
+ /* z-len tlv headers: allow, but don't process */
+ printf("z-len, skip unpack\n");
+ } else {
+ if ((type != xpct_type) ||
+ (len > xpct_len)) {
+ printf("xtlv_unpack Error: found[type:%d,len:%d] != xpct[type:%d,len:%d]\n",
+ type, len, xpct_type, xpct_len);
+ return BCME_BADARG;
+ }
+ /* copy tlv record to caller's buffer */
+ memcpy(dst, ptlv->data, ptlv->len);
+ }
+ *tlv_buf += BCM_XTLV_SIZE(ptlv, opts);
+ return BCME_OK;
+}
+
+/*
+ * packs user data into tlv record
+ * 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, void *src,
+ bcm_xtlv_opts_t opts)
+{
+ bcm_xtlv_t *ptlv = (bcm_xtlv_t *)*tlv_buf;
+ int size;
+
+ ASSERT(ptlv);
+ ASSERT(src);
+
+ size = bcm_xtlv_size_for_data(len, opts);
+
+ /* copy data from tlv buffer to dst provided by user */
+ if (size > *buflen) {
+ printf("bcm_pack_xtlv_entry: no space tlv_buf: requested:%d, available:%d\n",
+ size, *buflen);
+ return BCME_BADLEN;
+ }
+ ptlv->id = htol16(type);
+ ptlv->len = htol16(len);
+
+ /* copy callers data */
+ memcpy(ptlv->data, src, len);
+
+ /* advance callers pointer to tlv buff */
+ *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, 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;
+ bcm_xtlv_t *ptlv;
+ int sbuflen = buflen;
+
+ ASSERT(!buflen || tlv_buf);
+ ASSERT(!buflen || cbfn);
+
+ while (sbuflen >= (int)BCM_XTLV_HDR_SIZE) {
+ ptlv = (bcm_xtlv_t *)tlv_buf;
+
+ /* tlv header is always packed in LE order */
+ len = ltoh16(ptlv->len);
+ type = ltoh16(ptlv->id);
+
+ size = bcm_xtlv_size_for_data(len, opts);
+
+ sbuflen -= size;
+ /* check for possible buffer overrun */
+ if (sbuflen < 0)
+ break;
+
+ if ((res = cbfn(ctx, ptlv->data, type, len)) != BCME_OK)
+ break;
+ tlv_buf += size;
+ }
+ return res;
+}
+
+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)
+{
+ int res = BCME_OK;
+ uint16 tlv_id;
+ uint16 tlv_len;
+ uint8 *startp;
+ uint8 *endp;
+ uint8 *buf;
+ bool more;
+ int size;
+
+ ASSERT(get_next && pack_next);
+
+ buf = (uint8 *)tlv_buf;
+ startp = buf;
+ endp = (uint8 *)buf + buflen;
+ more = TRUE;
+ 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;
+ }
+
+ htol16_ua_store(tlv_id, buf);
+ htol16_ua_store(tlv_len, buf + sizeof(tlv_id));
+ pack_next(ctx, tlv_id, tlv_len, buf + BCM_XTLV_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(void **tlv_buf, uint16 *buflen, xtlv_desc_t *items,
+ bcm_xtlv_opts_t opts)
+{
+ int res = BCME_OK;
+ uint8 *ptlv = (uint8 *)*tlv_buf;
+
+ while (items->type != 0) {
+ if ((res = bcm_pack_xtlv_entry(&ptlv,
+ buflen, items->type,
+ items->len, items->ptr, opts) != 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(void *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 = ltoh16(elt->len);
+
+ while (dst_desc->type != 0) {
+ if (ltoh16(elt->id) == dst_desc->type) {
+ if (len != dst_desc->len) {
+ res = BCME_BADLEN;
+ } else {
+ memcpy(dst_desc->ptr, elt->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 *data_len_out will contain
+ * the the data length of the xTLV ID.
+ */
+void *
+bcm_get_data_from_xtlv_buf(uint8 *tlv_buf, uint16 buflen, uint16 id,
+ uint16 *datalen_out, bcm_xtlv_opts_t opts)
+{
+ void *retptr = NULL;
+ uint16 type, len;
+ int size;
+ bcm_xtlv_t *ptlv;
+ int sbuflen = buflen;
+
+ while (sbuflen >= (int)BCM_XTLV_HDR_SIZE) {
+ ptlv = (bcm_xtlv_t *)tlv_buf;
+
+ /* tlv header is always packed in LE order */
+ type = ltoh16(ptlv->id);
+ len = ltoh16(ptlv->len);
+ size = bcm_xtlv_size_for_data(len, opts);
+
+ sbuflen -= size;
+ /* check for possible buffer overrun */
+ if (sbuflen < 0) {
+ printf("%s %d: Invalid sbuflen %d\n",
+ __FUNCTION__, __LINE__, sbuflen);
+ break;
+ }
+
+ if (id == type) {
+ retptr = ptlv->data;
+ if (datalen_out) {
+ *datalen_out = len;
+ }
+ break;
+ }
+ tlv_buf += size;
+ }
+
+ return retptr;
+}
+
+int bcm_xtlv_size(const bcm_xtlv_t *elt, bcm_xtlv_opts_t opts)
+{
+ int size; /* entire size of the XTLV including header, data, and optional padding */
+ int len; /* XTLV's value real length wthout padding */
+
+ len = BCM_XTLV_LEN(elt);
+
+ size = bcm_xtlv_size_for_data(len, opts);
+
+ return size;
+}