1 // Copyright 2024 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 #pragma once 15 16 #include <cstdint> 17 #include <memory> 18 19 #include "pw_async2/dispatcher.h" 20 #include "pw_async2/once_sender.h" 21 #include "pw_bluetooth/internal/raii_ptr.h" 22 #include "pw_bluetooth/low_energy/advertising_data.h" 23 #include "pw_bluetooth/low_energy/connection2.h" 24 #include "pw_bluetooth/low_energy/phy.h" 25 #include "pw_bluetooth/types.h" 26 #include "pw_function/function.h" 27 #include "pw_result/expected.h" 28 29 namespace pw::bluetooth::low_energy { 30 31 /// `AdvertisedPeripheral` instances are valid for the duration of advertising. 32 class AdvertisedPeripheral2 { 33 public: 34 virtual ~AdvertisedPeripheral2() = default; 35 36 /// For connectable advertisements, this method returns Ready when an LE 37 /// central connects to the advertisement. 38 /// 39 /// The returned Connection2 can be used to interact with the peer. It also 40 /// represents a peripheral's ownership over the connection: the client can 41 /// drop the object to request a disconnection. Similarly, the Connection2 42 /// error handler is called by the system to indicate that the connection to 43 /// the peer has been lost. While connections are exclusive among peripherals, 44 /// they may be shared with centrals, preventing disconnections. 45 /// 46 /// After a connection is returned, advertising will be paused until 47 /// `PendConnection()` is called again. This method may return multiple 48 /// connections over the lifetime of an advertisement. 49 /// 50 /// @param cx Awoken when a connection is established. 51 virtual async2::Poll<Connection2::Ptr> PendConnection( 52 async2::Context& cx) = 0; 53 54 /// Requests that advertising be stopped. `PendStop()` can be used to wait for 55 /// advertising to stop (e.g. before starting another advertisement). 56 /// Destroying this object will also stop advertising, but there will be no 57 /// way to determine when advertising has stopped. This method is idempotent. 58 virtual void StopAdvertising() = 0; 59 60 /// Returns Ready when advertising has stopped due to a call to 61 /// `StopAdvertising()` or due to error. Awakens `cx` on stopping. 62 /// 63 /// @return @rst 64 /// 65 /// .. pw-status-codes:: 66 /// 67 /// OK: Advertising was stopped successfully after a call to 68 /// ``StopAdvertising()``. 69 /// 70 /// CANCELLED: An internal error occurred and the advertisement was 71 /// cancelled. 72 /// 73 /// @endrst 74 virtual async2::Poll<pw::Status> PendStop(async2::Context& cx) = 0; 75 76 private: 77 /// Stop advertising and release memory. This method is called by the 78 /// ~AdvertisedPeripheral::Ptr() when it goes out of scope, the API client 79 /// should never call this method. 80 virtual void Release() = 0; 81 82 public: 83 /// Movable `AdvertisedPeripheral2` smart pointer. The peripheral will 84 /// continue advertising until the returned `AdvertisedPeripheral::Ptr` is 85 /// destroyed. 86 using Ptr = 87 internal::RaiiPtr<AdvertisedPeripheral2, &AdvertisedPeripheral2::Release>; 88 }; 89 90 /// Represents the LE Peripheral role, which advertises and is connected to. 91 class Peripheral2 { 92 public: 93 /// The range of the time interval between advertisements. Shorter intervals 94 /// result in faster discovery at the cost of higher power consumption. The 95 /// exact interval used is determined by the Bluetooth controller. 96 /// - Time = N * 0.625ms. 97 /// - Time range: 0x0020 (20ms) - 0x4000 (10.24s) 98 struct AdvertisingIntervalRange { 99 /// Default: 1.28s 100 uint16_t min = 0x0800; 101 /// Default: 1.28s 102 uint16_t max = 0x0800; 103 }; 104 105 /// The fields that are to be sent in a scan response packet. Clients may 106 /// use this to send additional data that does not fit inside an advertising 107 /// packet on platforms that do not support the advertising data length 108 /// extensions. 109 /// 110 /// If present, advertisements will be configured to be scannable. 111 using ScanResponse = AdvertisingData; 112 113 /// If present, the controller will broadcast connectable advertisements 114 /// which allow peers to initiate connections to the Peripheral. The fields 115 /// of `ConnectionOptions` will configure any connections set up from 116 /// advertising. 117 using ConnectionOptions = Connection2::ConnectionOptions; 118 119 /// Use legacy advertising PDUs. Use this if you need compatibility with old 120 /// devices. 121 struct LegacyAdvertising { 122 /// See `ScanResponse` documentation. 123 std::optional<ScanResponse> scan_response; 124 125 /// See `ConnectionOptions` documentation. 126 std::optional<ConnectionOptions> connection_options; 127 }; 128 129 /// Advertise using the newer extended advertising Protocol Data Unit (PDU), 130 /// which aren't supported by older devices. 131 struct ExtendedAdvertising { 132 /// Anonymous advertisements do not include the address. 133 struct Anonymous {}; 134 135 /// Extended advertisements can have a scan response, be connectable, be 136 /// anonymous, or none of the above. See `ScanResponse`, 137 /// `ConnectionOptions`, and `Anonymous` documentation. 138 std::variant<std::monostate, ScanResponse, ConnectionOptions, Anonymous> 139 configuration; 140 141 /// The maximum power level to transmit with. Null indicates no preference. 142 /// Range: -127 to +20 143 /// Units: dBm 144 std::optional<int8_t> tx_power; 145 146 /// The primary physical layer configuration to advertise with. Can only be 147 /// 1Megabit or LeCoded PHY. If the PHY is not supported, a `kNotSupported` 148 /// error will be returned. 149 Phy primary_phy = Phy::k1Megabit; 150 151 /// The secondary physical layer configuration to advertise with. Can be any 152 /// PHY. If the PHY is not supported, a `kNotSupported` error will be 153 /// returned. 154 Phy secondary_phy = Phy::k1Megabit; 155 }; 156 157 using AdvertisingProcedure = 158 std::variant<LegacyAdvertising, ExtendedAdvertising>; 159 160 /// Represents the parameters for configuring advertisements. 161 struct AdvertisingParameters { 162 /// The fields that will be encoded in the data section of advertising 163 /// packets. 164 AdvertisingData data; 165 166 /// See `AdvertisingIntervalRange` documentation. 167 AdvertisingIntervalRange interval_range; 168 169 /// The type of address to include in advertising packets. If null, the host 170 /// stack will select an address type. If the address type could not be used 171 /// (either because of controller error or host configuration), a `kFailed` 172 /// error wil be returned. 173 std::optional<Address::Type> address_type; 174 175 /// Specifies the advertising procedure to use and the parameters specific 176 /// to that procedure. 177 AdvertisingProcedure procedure; 178 }; 179 180 /// Errors returned by `Advertise`. 181 enum class AdvertiseError { 182 /// The operation or parameters requested are not supported on the current 183 /// hardware. 184 kNotSupported = 1, 185 186 /// The provided advertising data exceeds the maximum allowed length when 187 /// encoded. 188 kAdvertisingDataTooLong = 2, 189 190 /// The provided scan response data exceeds the maximum allowed length when 191 /// encoded. 192 kScanResponseDataTooLong = 3, 193 194 /// The requested parameters are invalid. 195 kInvalidParameters = 4, 196 197 /// The maximum number of simultaneous advertisements has already been 198 /// reached. 199 kNotEnoughAdvertisingSlots = 5, 200 201 /// Advertising could not be initiated due to a hardware or system error. 202 kFailed = 6, 203 }; 204 205 using AdvertiseResult = 206 pw::expected<AdvertisedPeripheral2::Ptr, AdvertiseError>; 207 208 virtual ~Peripheral2() = default; 209 210 /// Start advertising continuously as a LE peripheral. If advertising cannot 211 /// be initiated then `result_callback` will be called with an error. Once 212 /// started, advertising can be stopped by destroying the returned 213 /// `AdvertisedPeripheral::Ptr`. 214 /// 215 /// If the system supports multiple advertising, this may be called as many 216 /// times as there are advertising slots. To reconfigure an advertisement, 217 /// first close the original advertisement and then initiate a new 218 /// advertisement. 219 /// 220 /// @param parameters Parameters used while configuring the advertising 221 /// instance. 222 /// @return Asynchronously returns a result once advertising has started or 223 /// failed. On success, returns an `AdvertisedPeripheral2` that models the 224 /// lifetime of the advertisement. Destroying it will stop advertising. 225 virtual async2::OnceReceiver<AdvertiseResult> Advertise( 226 const AdvertisingParameters& parameters) = 0; 227 }; 228 229 } // namespace pw::bluetooth::low_energy 230