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