|  | /* SPDX-License-Identifier: GPL-2.0 */ | 
|  | /* | 
|  | * BlueZ - Bluetooth protocol stack for Linux | 
|  | * | 
|  | * Copyright (C) 2021 Intel Corporation | 
|  | */ | 
|  |  | 
|  | #include <linux/unaligned.h> | 
|  |  | 
|  | void eir_create(struct hci_dev *hdev, u8 *data); | 
|  |  | 
|  | u8 eir_create_adv_data(struct hci_dev *hdev, u8 instance, u8 *ptr, u8 size); | 
|  | u8 eir_create_scan_rsp(struct hci_dev *hdev, u8 instance, u8 *ptr); | 
|  | u8 eir_create_per_adv_data(struct hci_dev *hdev, u8 instance, u8 *ptr); | 
|  |  | 
|  | u8 eir_append_local_name(struct hci_dev *hdev, u8 *eir, u8 ad_len); | 
|  | u8 eir_append_appearance(struct hci_dev *hdev, u8 *ptr, u8 ad_len); | 
|  | u8 eir_append_service_data(u8 *eir, u16 eir_len, u16 uuid, u8 *data, | 
|  | u8 data_len); | 
|  |  | 
|  | static inline u16 eir_precalc_len(u8 data_len) | 
|  | { | 
|  | return sizeof(u8) * 2 + data_len; | 
|  | } | 
|  |  | 
|  | static inline u16 eir_append_data(u8 *eir, u16 eir_len, u8 type, | 
|  | u8 *data, u8 data_len) | 
|  | { | 
|  | eir[eir_len++] = sizeof(type) + data_len; | 
|  | eir[eir_len++] = type; | 
|  | memcpy(&eir[eir_len], data, data_len); | 
|  | eir_len += data_len; | 
|  |  | 
|  | return eir_len; | 
|  | } | 
|  |  | 
|  | static inline u16 eir_append_le16(u8 *eir, u16 eir_len, u8 type, u16 data) | 
|  | { | 
|  | eir[eir_len++] = sizeof(type) + sizeof(data); | 
|  | eir[eir_len++] = type; | 
|  | put_unaligned_le16(data, &eir[eir_len]); | 
|  | eir_len += sizeof(data); | 
|  |  | 
|  | return eir_len; | 
|  | } | 
|  |  | 
|  | static inline u16 eir_skb_put_data(struct sk_buff *skb, u8 type, u8 *data, u8 data_len) | 
|  | { | 
|  | u8 *eir; | 
|  | u16 eir_len; | 
|  |  | 
|  | eir_len	= eir_precalc_len(data_len); | 
|  | eir = skb_put(skb, eir_len); | 
|  | WARN_ON(sizeof(type) + data_len > U8_MAX); | 
|  | eir[0] = sizeof(type) + data_len; | 
|  | eir[1] = type; | 
|  | memcpy(&eir[2], data, data_len); | 
|  |  | 
|  | return eir_len; | 
|  | } | 
|  |  | 
|  | static inline void *eir_get_data(u8 *eir, size_t eir_len, u8 type, | 
|  | size_t *data_len) | 
|  | { | 
|  | size_t parsed = 0; | 
|  |  | 
|  | if (eir_len < 2) | 
|  | return NULL; | 
|  |  | 
|  | while (parsed < eir_len - 1) { | 
|  | u8 field_len = eir[0]; | 
|  |  | 
|  | if (field_len == 0) | 
|  | break; | 
|  |  | 
|  | parsed += field_len + 1; | 
|  |  | 
|  | if (parsed > eir_len) | 
|  | break; | 
|  |  | 
|  | if (eir[1] != type) { | 
|  | eir += field_len + 1; | 
|  | continue; | 
|  | } | 
|  |  | 
|  | /* Zero length data */ | 
|  | if (field_len == 1) | 
|  | return NULL; | 
|  |  | 
|  | if (data_len) | 
|  | *data_len = field_len - 1; | 
|  |  | 
|  | return &eir[2]; | 
|  | } | 
|  |  | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | void *eir_get_service_data(u8 *eir, size_t eir_len, u16 uuid, size_t *len); |