bcmxtlv.c 14 KB

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