1/* SPDX-License-Identifier: GPL-2.0 */
2/*
3 * BlueZ - Bluetooth protocol stack for Linux
4 *
5 * Copyright (C) 2021 Intel Corporation
6 */
7
8#include <asm/unaligned.h>
9
10void eir_create(struct hci_dev *hdev, u8 *data);
11
12u8 eir_create_adv_data(struct hci_dev *hdev, u8 instance, u8 *ptr);
13u8 eir_create_scan_rsp(struct hci_dev *hdev, u8 instance, u8 *ptr);
14u8 eir_create_per_adv_data(struct hci_dev *hdev, u8 instance, u8 *ptr);
15
16u8 eir_append_local_name(struct hci_dev *hdev, u8 *eir, u8 ad_len);
17u8 eir_append_appearance(struct hci_dev *hdev, u8 *ptr, u8 ad_len);
18u8 eir_append_service_data(u8 *eir, u16 eir_len, u16 uuid, u8 *data,
19			   u8 data_len);
20
21static inline u16 eir_precalc_len(u8 data_len)
22{
23	return sizeof(u8) * 2 + data_len;
24}
25
26static inline u16 eir_append_data(u8 *eir, u16 eir_len, u8 type,
27				  u8 *data, u8 data_len)
28{
29	eir[eir_len++] = sizeof(type) + data_len;
30	eir[eir_len++] = type;
31	memcpy(&eir[eir_len], data, data_len);
32	eir_len += data_len;
33
34	return eir_len;
35}
36
37static inline u16 eir_append_le16(u8 *eir, u16 eir_len, u8 type, u16 data)
38{
39	eir[eir_len++] = sizeof(type) + sizeof(data);
40	eir[eir_len++] = type;
41	put_unaligned_le16(data, &eir[eir_len]);
42	eir_len += sizeof(data);
43
44	return eir_len;
45}
46
47static inline u16 eir_skb_put_data(struct sk_buff *skb, u8 type, u8 *data, u8 data_len)
48{
49	u8 *eir;
50	u16 eir_len;
51
52	eir_len	= eir_precalc_len(data_len);
53	eir = skb_put(skb, eir_len);
54	WARN_ON(sizeof(type) + data_len > U8_MAX);
55	eir[0] = sizeof(type) + data_len;
56	eir[1] = type;
57	memcpy(&eir[2], data, data_len);
58
59	return eir_len;
60}
61
62static inline void *eir_get_data(u8 *eir, size_t eir_len, u8 type,
63				 size_t *data_len)
64{
65	size_t parsed = 0;
66
67	if (eir_len < 2)
68		return NULL;
69
70	while (parsed < eir_len - 1) {
71		u8 field_len = eir[0];
72
73		if (field_len == 0)
74			break;
75
76		parsed += field_len + 1;
77
78		if (parsed > eir_len)
79			break;
80
81		if (eir[1] != type) {
82			eir += field_len + 1;
83			continue;
84		}
85
86		/* Zero length data */
87		if (field_len == 1)
88			return NULL;
89
90		if (data_len)
91			*data_len = field_len - 1;
92
93		return &eir[2];
94	}
95
96	return NULL;
97}
98
99void *eir_get_service_data(u8 *eir, size_t eir_len, u16 uuid, size_t *len);
100