xref: /aosp_15_r20/external/pigweed/pw_bluetooth/public/pw_bluetooth/low_energy/peripheral2.h (revision 61c4878ac05f98d0ceed94b57d316916de578985)
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