xref: /freebsd/sys/contrib/dev/broadcom/brcm80211/brcmfmac/xtlv.c (revision 902136e0fe112383ec64d2ef43a446063b5e6417)
1b4c3e9b5SBjoern A. Zeeb // SPDX-License-Identifier: ISC
2b4c3e9b5SBjoern A. Zeeb /*
3b4c3e9b5SBjoern A. Zeeb  * Copyright (c) 2019 Broadcom
4b4c3e9b5SBjoern A. Zeeb  */
5b4c3e9b5SBjoern A. Zeeb 
6b4c3e9b5SBjoern A. Zeeb #include <linux/unaligned.h>
7b4c3e9b5SBjoern A. Zeeb 
8b4c3e9b5SBjoern A. Zeeb #include <linux/math.h>
9b4c3e9b5SBjoern A. Zeeb #include <linux/string.h>
10b4c3e9b5SBjoern A. Zeeb #include <linux/bug.h>
11*902136e0SBjoern A. Zeeb #if defined(__FreeBSD__)
12*902136e0SBjoern A. Zeeb #include <linux/kernel.h>
13*902136e0SBjoern A. Zeeb #include <asm/unaligned.h>
14*902136e0SBjoern A. Zeeb #endif
15b4c3e9b5SBjoern A. Zeeb 
16b4c3e9b5SBjoern A. Zeeb #include "xtlv.h"
17b4c3e9b5SBjoern A. Zeeb 
brcmf_xtlv_header_size(u16 opts)18b4c3e9b5SBjoern A. Zeeb static int brcmf_xtlv_header_size(u16 opts)
19b4c3e9b5SBjoern A. Zeeb {
20b4c3e9b5SBjoern A. Zeeb 	int len = (int)offsetof(struct brcmf_xtlv, data);
21b4c3e9b5SBjoern A. Zeeb 
22b4c3e9b5SBjoern A. Zeeb 	if (opts & BRCMF_XTLV_OPTION_IDU8)
23b4c3e9b5SBjoern A. Zeeb 		--len;
24b4c3e9b5SBjoern A. Zeeb 	if (opts & BRCMF_XTLV_OPTION_LENU8)
25b4c3e9b5SBjoern A. Zeeb 		--len;
26b4c3e9b5SBjoern A. Zeeb 
27b4c3e9b5SBjoern A. Zeeb 	return len;
28b4c3e9b5SBjoern A. Zeeb }
29b4c3e9b5SBjoern A. Zeeb 
brcmf_xtlv_data_size(int dlen,u16 opts)30b4c3e9b5SBjoern A. Zeeb int brcmf_xtlv_data_size(int dlen, u16 opts)
31b4c3e9b5SBjoern A. Zeeb {
32b4c3e9b5SBjoern A. Zeeb 	int hsz;
33b4c3e9b5SBjoern A. Zeeb 
34b4c3e9b5SBjoern A. Zeeb 	hsz = brcmf_xtlv_header_size(opts);
35b4c3e9b5SBjoern A. Zeeb 	if (opts & BRCMF_XTLV_OPTION_ALIGN32)
36b4c3e9b5SBjoern A. Zeeb 		return roundup(dlen + hsz, 4);
37b4c3e9b5SBjoern A. Zeeb 
38b4c3e9b5SBjoern A. Zeeb 	return dlen + hsz;
39b4c3e9b5SBjoern A. Zeeb }
40b4c3e9b5SBjoern A. Zeeb 
brcmf_xtlv_pack_header(struct brcmf_xtlv * xtlv,u16 id,u16 len,const u8 * data,u16 opts)41b4c3e9b5SBjoern A. Zeeb void brcmf_xtlv_pack_header(struct brcmf_xtlv *xtlv, u16 id, u16 len,
42b4c3e9b5SBjoern A. Zeeb 			    const u8 *data, u16 opts)
43b4c3e9b5SBjoern A. Zeeb {
44b4c3e9b5SBjoern A. Zeeb 	u8 *data_buf;
45b4c3e9b5SBjoern A. Zeeb 	u16 mask = BRCMF_XTLV_OPTION_IDU8 | BRCMF_XTLV_OPTION_LENU8;
46b4c3e9b5SBjoern A. Zeeb 
47b4c3e9b5SBjoern A. Zeeb 	if (!(opts & mask)) {
48b4c3e9b5SBjoern A. Zeeb 		u8 *idp = (u8 *)xtlv;
49b4c3e9b5SBjoern A. Zeeb 		u8 *lenp = idp + sizeof(xtlv->id);
50b4c3e9b5SBjoern A. Zeeb 
51b4c3e9b5SBjoern A. Zeeb 		put_unaligned_le16(id, idp);
52b4c3e9b5SBjoern A. Zeeb 		put_unaligned_le16(len, lenp);
53b4c3e9b5SBjoern A. Zeeb 		data_buf = lenp + sizeof(u16);
54b4c3e9b5SBjoern A. Zeeb 	} else if ((opts & mask) == mask) { /* u8 id and u8 len */
55b4c3e9b5SBjoern A. Zeeb 		u8 *idp = (u8 *)xtlv;
56b4c3e9b5SBjoern A. Zeeb 		u8 *lenp = idp + 1;
57b4c3e9b5SBjoern A. Zeeb 
58b4c3e9b5SBjoern A. Zeeb 		*idp = (u8)id;
59b4c3e9b5SBjoern A. Zeeb 		*lenp = (u8)len;
60b4c3e9b5SBjoern A. Zeeb 		data_buf = lenp + sizeof(u8);
61b4c3e9b5SBjoern A. Zeeb 	} else if (opts & BRCMF_XTLV_OPTION_IDU8) { /* u8 id, u16 len */
62b4c3e9b5SBjoern A. Zeeb 		u8 *idp = (u8 *)xtlv;
63b4c3e9b5SBjoern A. Zeeb 		u8 *lenp = idp + 1;
64b4c3e9b5SBjoern A. Zeeb 
65b4c3e9b5SBjoern A. Zeeb 		*idp = (u8)id;
66b4c3e9b5SBjoern A. Zeeb 		put_unaligned_le16(len, lenp);
67b4c3e9b5SBjoern A. Zeeb 		data_buf = lenp + sizeof(u16);
68b4c3e9b5SBjoern A. Zeeb 	} else if (opts & BRCMF_XTLV_OPTION_LENU8) { /* u16 id, u8 len */
69b4c3e9b5SBjoern A. Zeeb 		u8 *idp = (u8 *)xtlv;
70b4c3e9b5SBjoern A. Zeeb 		u8 *lenp = idp + sizeof(u16);
71b4c3e9b5SBjoern A. Zeeb 
72b4c3e9b5SBjoern A. Zeeb 		put_unaligned_le16(id, idp);
73b4c3e9b5SBjoern A. Zeeb 		*lenp = (u8)len;
74b4c3e9b5SBjoern A. Zeeb 		data_buf = lenp + sizeof(u8);
75b4c3e9b5SBjoern A. Zeeb 	} else {
76b4c3e9b5SBjoern A. Zeeb 		WARN(true, "Unexpected xtlv option");
77b4c3e9b5SBjoern A. Zeeb 		return;
78b4c3e9b5SBjoern A. Zeeb 	}
79b4c3e9b5SBjoern A. Zeeb 
80b4c3e9b5SBjoern A. Zeeb 	if (opts & BRCMF_XTLV_OPTION_LENU8) {
81b4c3e9b5SBjoern A. Zeeb 		WARN_ON(len > 0x00ff);
82b4c3e9b5SBjoern A. Zeeb 		len &= 0xff;
83b4c3e9b5SBjoern A. Zeeb 	}
84b4c3e9b5SBjoern A. Zeeb 
85b4c3e9b5SBjoern A. Zeeb 	if (data)
86b4c3e9b5SBjoern A. Zeeb 		memcpy(data_buf, data, len);
87b4c3e9b5SBjoern A. Zeeb }
88b4c3e9b5SBjoern A. Zeeb 
89