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 "pw_async2/dispatcher.h" 17 #include "pw_async2/once_sender.h" 18 #include "pw_bluetooth/gatt/constants.h" 19 #include "pw_bluetooth/gatt/error.h" 20 #include "pw_bluetooth/gatt/types.h" 21 #include "pw_bluetooth/internal/raii_ptr.h" 22 #include "pw_containers/vector.h" 23 #include "pw_function/function.h" 24 #include "pw_multibuf/multibuf.h" 25 #include "pw_result/expected.h" 26 27 namespace pw::bluetooth::gatt { 28 29 /// An interface for interacting with a GATT service on a peer device. 30 class RemoteService2 { 31 public: 32 enum class RemoteServiceError { 33 /// The service has been modified or removed. 34 kServiceRemoved = 0, 35 36 /// The peer serving this service has disconnected. 37 kPeerDisconnected = 1, 38 }; 39 40 /// Wrapper around a possible truncated value received from the server. 41 struct ReadValue { 42 /// Characteristic or descriptor handle. 43 Handle handle; 44 45 /// The value of the characteristic or descriptor. 46 multibuf::MultiBuf value; 47 48 /// True if `value` might be truncated (the buffer was completely filled by 49 /// the server and the read was a short read). `ReadCharacteristic` or 50 /// `ReadDescriptor` should be used to read the complete value. 51 bool maybe_truncated; 52 }; 53 54 /// A result returned by `ReadByType`. 55 struct ReadByTypeResult { 56 /// Characteristic or descriptor handle. 57 Handle handle; 58 59 /// The value of the characteristic or descriptor, if it was read 60 /// successfully, or an error explaining why the value could not be read. 61 pw::expected<ReadValue, Error> result; 62 }; 63 64 /// Represents the supported options to read a long characteristic or 65 /// descriptor value from a server. Long values are those that may not fit in 66 /// a single message. 67 struct LongReadOptions { 68 /// The byte to start the read at. Must be less than the length of the 69 /// value. 70 uint16_t offset = 0; 71 72 /// The maximum number of bytes to read. 73 uint16_t max_bytes = kMaxValueLength; 74 }; 75 76 /// Represents the supported write modes for writing characteristics & 77 /// descriptors to the server. 78 enum class WriteMode : uint8_t { 79 /// Wait for a response from the server before returning but do not verify 80 /// the echo response. Supported for both characteristics and descriptors. 81 kDefault, 82 83 /// Every value blob is verified against an echo response from the server. 84 /// The procedure is aborted if a value blob has not been reliably delivered 85 /// to the peer. Only supported for characteristics. 86 kReliable, 87 88 /// Delivery will not be confirmed before returning. Writing without a 89 /// response is only supported for short characteristics with the 90 /// `WRITE_WITHOUT_RESPONSE` property. The value must fit into a single 91 /// message. It is guaranteed that at least 20 bytes will fit into a single 92 /// message. If the value does not fit, a `kFailure` error will be produced. 93 /// The value will be written at offset 0. Only supported for 94 /// characteristics. 95 kWithoutResponse, 96 }; 97 98 /// Represents the supported options to write a characteristic/descriptor 99 /// value to a server. 100 struct WriteOptions { 101 /// The mode of the write operation. For descriptors, only 102 /// `WriteMode::kDefault` is supported 103 WriteMode mode = WriteMode::kDefault; 104 105 /// Request a write starting at the byte indicated. 106 /// Must be 0 if `mode` is `WriteMode.kWithoutResponse`. 107 uint16_t offset = 0; 108 }; 109 110 virtual ~RemoteService2() = default; 111 112 /// Poll for an Error status on this service, waking `cx` and returning 113 /// Ready when there is an error condition. When an error condition is 114 /// present, any previous `RemoteService2` `Waker` and `OnceReceiver` 115 /// instances may or may not be woken and all other methods will be no-ops. 116 /// Only 1 waker can be set at a time (additional calls will replace the 117 /// existing waker). 118 virtual async2::Poll<RemoteServiceError> PendError(async2::Context& cx) = 0; 119 120 /// Asynchronously sends the characteristics in this service, up to 121 /// `Vector::.max_size()`. May perform service discovery if the 122 /// characteristics are not yet known. 123 virtual void DiscoverCharacteristics( 124 async2::OnceRefSender<Vector<Characteristic2>> 125 characteristics_sender) = 0; 126 127 /// Reads characteristics and descriptors with the specified type. This method 128 /// is useful for reading values before discovery has completed, thereby 129 /// reducing latency. 130 /// @param uuid The UUID of the characteristics/descriptors to read. 131 /// @return The result of the read. Results may be empty if no matching values 132 /// are read. If reading a value results in a permission error, the handle and 133 /// error will be included. 134 /// 135 /// This may fail with the following errors: 136 /// - `kInvalidParameters`: if `uuid` refers to an internally reserved 137 /// descriptor type (e.g. the Client Characteristic Configuration descriptor). 138 /// - `kTooManyResults`: More results were read than can fit in the Vector. 139 /// Consider reading characteristics/descriptors individually after performing 140 /// discovery. 141 /// - `kFailure`: The server returned an error not specific to a single 142 /// result. 143 virtual async2::OnceReceiver<pw::expected<Vector<ReadByTypeResult, 5>, Error>> 144 ReadByType(Uuid uuid) = 0; 145 146 /// Reads the value of a characteristic. 147 /// @param handle The handle of the characteristic to be read. 148 /// @param options If null, a short read will be performed, which may be 149 /// truncated to what fits in a single message (at least 22 bytes). If long 150 /// read options are present, performs a long read with the indicated options. 151 /// @return The result of the read and the value of the characteristic if 152 /// successful. 153 /// Returns the following errors: 154 /// - kInvalidHandle `handle` is invalid. 155 /// - kInvalidParameters `options` is invalid. 156 /// - kReadNotPermitted The server rejected the request. 157 /// - kInsufficient* The server rejected the request. 158 /// - kApplicationError* An application error was returned by the GATT 159 /// profile. 160 /// - kFailure The server returned an error not covered by the above. 161 virtual async2::OnceReceiver<pw::expected<ReadValue, Error>> 162 ReadCharacteristic(Handle handle, std::optional<LongReadOptions> options) = 0; 163 164 /// Writes `value` to the characteristic with `handle` using the provided 165 /// `options`. 166 /// 167 /// @param handle Handle of the characteristic to be written to 168 /// @param value The value to be written. 169 /// @param options Options that apply to the write. 170 /// @return A result is returned when a response to the write is 171 /// received. For WriteWithoutResponse, this is set as soon as the write 172 /// is sent. 173 /// Returns the following errors: 174 /// - kInvalidHandle `handle` is invalid. 175 /// - kInvalidParameters`options is invalid. 176 /// - kWriteNotPermitted The server rejected the request. 177 /// - kInsufficient* The server rejected the request. 178 /// - kApplicationError* An application error was returned by the GATT 179 /// profile. 180 /// - kFailure The server returned an error not covered by the above 181 /// errors. 182 virtual async2::OnceReceiver<pw::expected<void, Error>> WriteCharacteristic( 183 Handle handle, pw::multibuf::MultiBuf&& value, WriteOptions options) = 0; 184 185 /// Reads the value of the characteristic descriptor with `handle` and 186 /// returns it in the reply. 187 /// @param handle The descriptor handle to read. 188 /// @param options Options that apply to the read. 189 /// @param result_sender Set to a result containing the value of the 190 /// descriptor on success. 191 /// @retval kInvalidHandle `handle` is invalid. 192 /// @retval kInvalidParameters `options` is invalid. 193 /// @retval kReadNotPermitted 194 /// @retval kInsufficient* The server rejected the request. 195 /// @retval kApplicationError* An application error was returned by the GATT 196 /// profile. 197 /// @retval kFailure The server returned an error not covered by the above 198 /// errors. 199 virtual async2::OnceReceiver<pw::expected<ReadValue, Error>> ReadDescriptor( 200 Handle handle, std::optional<LongReadOptions> options) = 0; 201 202 /// Writes `value` to the descriptor with `handle` using the provided. It is 203 /// not recommended to send additional writes while a write is already in 204 /// progress. 205 /// 206 /// @param handle Handle of the descriptor to be written to. 207 /// @param value The value to be written. 208 /// @return The result upon completion of the write. 209 /// Possible errors: 210 /// - kInvalidHandle `handle` is invalid. 211 /// - kInvalidParameters `options is invalid. 212 /// - kWriteNotPermitted The server rejected the request. 213 /// - kInsufficient* The server rejected the request. 214 /// - kApplicationError* An application error was returned by the GATT 215 /// profile. 216 /// - kFailure The server returned an error not covered by the above 217 /// errors. 218 virtual async2::OnceReceiver<pw::expected<void, Error>> WriteDescriptor( 219 Handle handle, pw::multibuf::MultiBuf&& value) = 0; 220 221 /// Subscribe to notifications & indications from the characteristic with 222 /// the given `handle`. 223 /// 224 /// Either notifications or indications will be enabled depending on 225 /// characteristic properties. Indications will be preferred if they are 226 /// supported. This operation fails if the characteristic does not have the 227 /// "notify" or "indicate" property. 228 /// 229 /// A write request will be issued to configure the characteristic for 230 /// notifications/indications if it contains a Client Characteristic 231 /// Configuration (CCC) descriptor. This method fails if an error occurs while 232 /// writing to the descriptor. 233 /// 234 /// On success, `PendNotification` will return Ready when the peer sends a 235 /// notification or indication. Indications are automatically confirmed. 236 /// 237 /// Subscriptions can be canceled with `StopNotifications`. 238 /// 239 /// @param handle the handle of the characteristic to subscribe to. 240 /// @return The result of enabling notifications/indications. 241 /// - kFailure The characteristic does not support notifications or 242 /// indications. 243 /// - kInvalidHandle `handle` is invalid. 244 /// - kWriteNotPermitted CCC descriptor write error. 245 /// - kInsufficient* Insufficient security properties to write to CCC 246 /// descriptor. 247 virtual async2::OnceReceiver<pw::expected<void, Error>> EnableNotifications( 248 Handle handle) = 0; 249 250 /// After notifications have been enabled with `EnableNotifications`, this 251 /// method can be used to check for notifications. This method will safely 252 /// return Pending when notifications are disabled. 253 /// @param handle The handle of the characteristic to await for notifications. 254 /// @param cx The Context to awaken when a notification is available. Only 255 /// one Waker per handle is supported at a time (subsequent calls will 256 /// overwrite the old Waker). 257 virtual async2::Poll<ReadValue> PendNotification(Handle handle, 258 async2::Context& cx) = 0; 259 260 /// Stops notifications for the characteristic with the given `handle`. 261 /// @return The result of disabling notifications/indications. 262 /// Possible errors: 263 /// - kFailure The characteristic does not support notifications or 264 /// indications. 265 /// - kInvalidHandle `handle` is invalid. 266 /// - kWriteNotPermitted CCC descriptor write error. 267 /// - Insufficient* CCC descriptor write error. 268 virtual async2::OnceReceiver<pw::expected<void, Error>> StopNotifications( 269 Handle handle) = 0; 270 271 private: 272 /// Disconnect from the remote service. This method is called by the 273 /// ~RemoteService::Ptr() when it goes out of scope, the API client should 274 /// never call this method. 275 virtual void Disconnect() = 0; 276 277 public: 278 /// Movable `RemoteService2` smart pointer. The remote server will remain 279 /// connected until the returned `RemoteService2::Ptr` is destroyed. 280 using Ptr = internal::RaiiPtr<RemoteService2, &RemoteService2::Disconnect>; 281 }; 282 283 /// Represents a GATT client that interacts with services on a GATT server. 284 class Client2 { 285 public: 286 /// Represents a remote GATT service. 287 struct RemoteServiceInfo { 288 /// Uniquely identifies this GATT service. 289 ServiceHandle handle; 290 291 /// Indicates whether this is a primary or secondary service. 292 bool primary; 293 294 /// The UUID that identifies the type of this service. 295 /// There may be multiple services with the same UUID. 296 Uuid type; 297 }; 298 299 virtual ~Client2() = default; 300 301 /// Enumerates existing services found on the peer that this object 302 /// represents and notifies of modifications to services or new services 303 /// thereafter. If service discovery hasn't happened yet, it may be started. 304 /// To further interact with services, clients must obtain a `RemoteService2` 305 /// protocol by calling `ConnectToService`. 306 /// @return Will return `Ready` with `RemoteServiceInfo` when there are 307 /// services that are updated/modified. This can can be called repeatedly 308 /// until `Pending` is returned to get all previously discovered services. 309 virtual async2::Poll<RemoteServiceInfo> PendServiceUpdate( 310 async2::Context& cx); 311 312 /// Returns the handles of services that have been removed. Note that handles 313 /// may be reused, so it is recommended to check for removed services before 314 /// calling `PendServiceUpdate`. This should be called repeatedly until 315 /// `Pending` is returned. 316 /// 317 /// @param cx Awoken when a service is removed after Pending is returned. 318 virtual async2::Poll<ServiceHandle> PendServiceRemoved(async2::Context& cx); 319 320 /// Connects to a `RemoteService2`. Only 1 connection per service is allowed. 321 /// 322 /// @param handle The handle of the service to connect to. 323 /// 324 /// @return kInvalidParameters `handle` does not correspond to a known 325 /// service. 326 virtual pw::expected<RemoteService2::Ptr, Error> ConnectToService( 327 ServiceHandle handle) = 0; 328 }; 329 330 } // namespace pw::bluetooth::gatt 331