1 // Copyright 2023 The Pigweed Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not 4 // use this file except in compliance with the License. You may obtain a copy of 5 // the License at 6 // 7 // https://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 // License for the specific language governing permissions and limitations under 13 // the License. 14 15 #pragma once 16 #include "pw_bluetooth_sapphire/internal/host/hci-spec/protocol.h" 17 18 namespace bt::hci { 19 20 // Extended advertising HCI commands refer to a particular advertising set via 21 // an AdvertisingHandle. An AdvertisingHandle is an eight bit unsigned integer 22 // that uniquely identifies an advertising set and vice versa. This means that 23 // we frequently need to convert between a DeviceAddress and an 24 // AdvertisingHandle. AdvertisingHandleMap provides a 1:1 bidirectional mapping 25 // between a DeviceAddress and an AdvertisingHandle, allocating the next 26 // available AdvertisingHandle to new DeviceAddresses. 27 // 28 // When using extended advertising, there are two types of advertising PDU 29 // formats available: legacy PDUs and extended PDUs. Legacy advertising PDUs are 30 // currently the most widely compatible type, discoverable by devices deployed 31 // prior to the adoption of Bluetooth 5.0. Devices deployed more recently are 32 // also able to discover this type of advertising packet. Conversely, extended 33 // advertising PDUs are a newer format that offers a number of improvements, 34 // including the ability to advertise larger amounts of data. However, devices 35 // not specifically scanning for them or who are running on an older version of 36 // Bluetooth (pre-5.0) won't be able to see them. 37 // 38 // When advertising using extended advertising PDUs, users often choose to emit 39 // legacy advertising PDUs as well in order to maintain backwards compatibility 40 // with older Bluetooth devices. As such, advertisers such as 41 // ExtendedLowEnergyAdvertiser may need to track two real AdvertisingHandles 42 // for each logical advertisement, one for legacy advertising PDUs and one for 43 // extended advertising PDUs. Along with DeviceAddress, AdvertisingHandleMap 44 // tracks whether the mapping is for an extended PDU or a legacy PDU. 45 // 46 // Users shouldn't rely on any particular ordering of the next available 47 // mapping. Any available AdvertisingHandle may be used. 48 class AdvertisingHandleMap { 49 public: 50 // Instantiate an AdvertisingHandleMap. The capacity parameter specifies the 51 // maximum number of mappings that this instance will support. Setting the 52 // capacity also restricts the range of advertising handles 53 // AdvertisingHandleMap will return: [0, capacity). 54 explicit AdvertisingHandleMap( 55 uint8_t capacity = hci_spec::kMaxAdvertisingHandle + 1) capacity_(capacity)56 : capacity_(capacity) {} 57 58 // Convert a DeviceAddress to an AdvertisingHandle, creating the mapping if it 59 // doesn't already exist. The conversion may fail if there are already 60 // hci_spec::kMaxAdvertisingHandles in the container. 61 std::optional<hci_spec::AdvertisingHandle> MapHandle( 62 const DeviceAddress& address, bool extended_pdu); 63 64 // Convert a DeviceAddress to an AdvertisingHandle. The conversion may fail if 65 // there is no AdvertisingHandle currently mapping to the provided device 66 // address. 67 std::optional<hci_spec::AdvertisingHandle> GetHandle( 68 const DeviceAddress& address, bool extended_pdu) const; 69 70 // Convert an AdvertisingHandle to a DeviceAddress. The conversion may fail if 71 // there is no DeviceAddress currently mapping to the provided handle. 72 std::optional<DeviceAddress> GetAddress( 73 hci_spec::AdvertisingHandle handle) const; 74 75 // Remove the mapping between an AdvertisingHandle and the DeviceAddress it 76 // maps to. The container may reuse the AdvertisingHandle for other 77 // DeviceAddresses in the future. Immediate future calls to GetAddress(...) 78 // with the same AdvertisingHandle will fail because the mapping no longer 79 // exists. Immediate future calls to GetHandle(...) will result in a new 80 // mapping with a new AdvertisingHandle. 81 // 82 // If the given handle doesn't map to any (DeviceAddress, bool) tuple, this 83 // function does nothing. 84 void RemoveHandle(hci_spec::AdvertisingHandle handle); 85 86 // Remove the mapping between a DeviceAddress and the AdvertisingHandle it 87 // maps to. The container may reuse the AdvertisingHandle for other 88 // DeviceAddresses in the future. Immediate future calls to GetAddress(...) 89 // with the preivously mapped AdvertisingHandle will fail because the mapping 90 // no longer exists. Immediate future calls to GetHandle(...) will result in a 91 // new mapping with a new AdvertisingHandle. 92 // 93 // If the given (DeviceAddress, bool) tuple doesn't map to any 94 // AdvertisingHandle, this function does nothing. 95 void RemoveAddress(const DeviceAddress& address, bool extended_pdu); 96 97 // Get the maximum number of mappings the AdvertisingHandleMap will support. capacity()98 uint8_t capacity() const { return capacity_; } 99 100 // Retrieve the advertising handle that was most recently generated. This 101 // function is primarily used by unit tests so as to avoid hardcoding values 102 // or making assumptions about the starting point or ordering of advertising 103 // handle generation. 104 std::optional<hci_spec::AdvertisingHandle> LastUsedHandleForTesting() const; 105 106 // Get the number of unique mappings in the container 107 std::size_t Size() const; 108 109 // Return true if the container has no mappings, false otherwise 110 bool Empty() const; 111 112 // Remove all mappings in the container 113 void Clear(); 114 115 private: 116 // Although not in the range of valid advertising handles (0x00 to 0xEF), 117 // kStartHandle is chosen to be 0xFF because adding one to it will overflow to 118 // 0, the first valid advertising handle. 119 constexpr static hci_spec::AdvertisingHandle kStartHandle = 0xFF; 120 121 // Tracks the maximum number of elements that can be stored in this container. 122 // 123 // NOTE: AdvertisingHandles have a range of [0, capacity_). This value isn't 124 // set using default member initialization because it is set within the 125 // constructor itself. 126 uint8_t capacity_; 127 128 // Generate the next valid, available, and within range AdvertisingHandle. 129 // This function may fail if there are already 130 // hci_spec::kMaxAdvertisingHandles in the container: there are no more valid 131 // AdvertisingHandles. 132 std::optional<hci_spec::AdvertisingHandle> NextHandle(); 133 134 // The last generated advertising handle used as a hint to generate the next 135 // handle; defaults to kStartHandle if no handles have been generated yet. 136 hci_spec::AdvertisingHandle last_handle_ = kStartHandle; 137 138 struct TupleKeyHasher { operatorTupleKeyHasher139 size_t operator()(const std::tuple<DeviceAddress, bool>& t) const { 140 std::hash<DeviceAddress> device_address_hasher; 141 std::hash<bool> bool_hasher; 142 const auto& [address, extended_pdu] = t; 143 return device_address_hasher(address) ^ bool_hasher(extended_pdu); 144 } 145 }; 146 147 std::unordered_map<std::tuple<DeviceAddress, bool>, 148 hci_spec::AdvertisingHandle, 149 TupleKeyHasher> 150 addr_to_handle_; 151 152 std::unordered_map<hci_spec::AdvertisingHandle, 153 std::tuple<DeviceAddress, bool>> 154 handle_to_addr_; 155 }; 156 157 } // namespace bt::hci 158