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 18 #include "pw_async2/dispatcher.h" 19 #include "pw_async2/once_sender.h" 20 #include "pw_bluetooth/gatt/error.h" 21 #include "pw_bluetooth/gatt/types.h" 22 #include "pw_bluetooth/internal/raii_ptr.h" 23 #include "pw_bluetooth/types.h" 24 #include "pw_multibuf/multibuf.h" 25 #include "pw_result/expected.h" 26 #include "pw_span/span.h" 27 28 namespace pw::bluetooth::gatt { 29 30 /// Interface for serving a local GATT service. This is implemented by the API 31 /// client. 32 class LocalServiceDelegate2 { 33 public: 34 virtual ~LocalServiceDelegate2() = default; 35 36 /// Called when there is a fatal error related to this service that forces the 37 /// service to close. LocalServiceDelegate methods will no longer be called. 38 /// This invalidates the associated LocalService. It is OK to destroy both 39 /// `LocalServiceDelegate` and the associated `LocalService::Ptr` from within 40 /// this method. 41 virtual void OnError(Error error) = 0; 42 43 /// This notifies the current configuration of a particular 44 /// characteristic/descriptor for a particular peer. It will be called when 45 /// the peer GATT client changes the configuration. 46 /// 47 /// The Bluetooth stack maintains the state of each peer's configuration 48 /// across reconnections. As such, this method will be called with both 49 /// `notify` and `indicate` set to false for each characteristic when a peer 50 /// disconnects. Also, when a peer reconnects this method will be called again 51 /// with the persisted state of the newly-connected peer's configuration. 52 /// However, clients should not rely on this state being persisted 53 /// indefinitely by the Bluetooth stack. 54 /// 55 /// @param peer_id The PeerId of the GATT client associated with this 56 /// particular CCC. 57 /// @param handle The handle of the characteristic associated with the 58 /// `notify` and `indicate` parameters. 59 /// @param notify True if the client has enabled notifications, false 60 /// otherwise. 61 /// @param indicate True if the client has enabled indications, false 62 /// otherwise. 63 virtual void CharacteristicConfiguration(PeerId peer_id, 64 Handle handle, 65 bool notify, 66 bool indicate) = 0; 67 68 /// Called when a peer requests to read the value of a characteristic or 69 /// descriptor. It is guaranteed that the peer satisfies the permissions 70 /// associated with this attribute. 71 /// 72 /// @param peer_id The PeerId of the GATT client making the read request. 73 /// @param handle The handle of the requested descriptor/characteristic. 74 /// @param offset The offset at which to start reading the requested value. 75 /// @return Returns the value of the characteristic on success, or an Error on 76 /// failure. The value will be truncated to fit in the MTU if necessary. 77 virtual async2::OnceReceiver<pw::expected<multibuf::MultiBuf, Error>> 78 ReadValue(PeerId peer_id, Handle handle, uint32_t offset) = 0; 79 80 /// Called when a peer issues a request to write the value of a characteristic 81 /// or descriptor. It is guaranteed that the peer satisfies the permissions 82 /// associated with this attribute. 83 /// 84 /// @param peer_id The PeerId of the GATT client making the write request. 85 /// @param handle The handle of the requested descriptor/characteristic. 86 /// @param offset The offset at which to start writing the requested value. If 87 /// the offset is 0, any existing value should be overwritten by the new 88 /// value. Otherwise, the existing value between `offset:(offset + 89 /// len(value))` should be changed to `value`. 90 /// @param value The new value for the descriptor/characteristic. 91 /// @return The result of the write. 92 virtual async2::OnceReceiver<pw::expected<void, Error>> WriteValue( 93 PeerId peer_id, 94 Handle handle, 95 uint32_t offset, 96 multibuf::MultiBuf&& value) = 0; 97 98 /// Called when the MTU of a peer is updated. Also called for peers that are 99 /// already connected when the server is published. 100 /// 101 /// Notifications and indications must fit in a single packet including both 102 /// the 3-byte notification/indication header and the user-provided payload. 103 /// If these are not used, the MTU can be safely ignored as it is intended for 104 /// use cases where the throughput needs to be optimized. 105 virtual void MtuUpdate(PeerId peer_id, uint16_t mtu) = 0; 106 }; 107 108 /// Interface provided by the backend to interact with a published service. 109 /// LocalService is valid for the lifetime of a published GATT service. It is 110 /// used to control the service and send notifications/indications. 111 class LocalService2 { 112 public: 113 /// The parameters used to signal a characteristic value change from a 114 /// LocalService to a peer. 115 struct ValueChangedParameters { 116 /// The peers peers to signal. The LocalService should respect the 117 /// Characteristic Configuration associated with a peer+handle when deciding 118 /// whether to signal it. If empty, all peers which configured the handle 119 /// are signalled. 120 pw::span<const PeerId> peer_ids; 121 /// The handle of the characteristic value being signaled. 122 Handle handle; 123 /// The new value for the descriptor/characteristic. 124 multibuf::MultiBuf value; 125 }; 126 127 /// The Result type for a ValueChanged indication or notification message. The 128 /// error can be locally generated for notifications and either locally or 129 /// remotely generated for indications. 130 using ValueChangedResult = pw::expected<void, Error>; 131 132 virtual ~LocalService2() = default; 133 134 /// Sends a notification to peers. Notifications should be used instead of 135 /// indications when the service does *not* require peer confirmation of the 136 /// update. 137 /// 138 /// Notifications should not be sent to peers which have not enabled 139 /// notifications on a particular characteristic or that have disconnected. 140 /// If notifications are sent, they will not be propagated and the 141 /// `result_sender` will be set to an error condition. The Bluetooth stack 142 /// will track this configuration for the lifetime of the service. 143 /// 144 /// The maximum size of the `parameters.value` field depends on the Maximum 145 /// Transmission Unit (MTU) negotiated with the peer. A 3-byte header plus the 146 /// value contents must fit in a packet of MTU bytes. 147 /// 148 /// @param parameters The parameters associated with the changed 149 /// characteristic. 150 /// @return The result is returned when the notification has been sent to 151 /// all peers or an error is produced when trying to send the notification to 152 /// any of the peers. This value is only set only once when all associated 153 /// work is done, if the implementation wishes to receive a call on a 154 /// per-peer basis, they should send this event with a single PeerId in 155 /// `parameters.peer_ids`. Additional values should not be notified until 156 /// this notification completes. 157 virtual async2::OnceReceiver<ValueChangedResult> NotifyValue( 158 ValueChangedParameters&& parameters) = 0; 159 160 /// Sends an indication to peers. Indications should be used instead of 161 /// notifications when the service *does* require peer confirmation of the 162 /// update. 163 /// 164 /// Indications should not be sent to peers which have not enabled indications 165 /// on a particular characteristic. If they are sent, they will not be 166 /// propagated and the `result_sender` will be set to an error condition. The 167 /// Bluetooth stack will track this configuration for the lifetime of the 168 /// service. 169 /// 170 /// If any of the peers in `parameters.peer_ids` fails to confirm the 171 /// indication within the ATT transaction timeout (30 seconds per 172 /// Bluetooth 6.0 Vol. 3 Part F 3.3.3), the link between the peer and the 173 /// local adapter will be closed. 174 /// 175 /// The maximum size of the `parameters.value` field depends on the MTU 176 /// negotiated with the peer. A 3-byte header plus the value contents must fit 177 /// in a packet of MTU bytes. 178 /// 179 /// @param parameters The parameters associated with the changed 180 /// characteristic. 181 /// @return When all the peers listed in `parameters.peer_ids` 182 /// have confirmed the indication, the result is returned. If the 183 /// implementation wishes to receive indication confirmations on a per-peer 184 /// basis, they should send this event with a single PeerId in 185 /// `parameters.peer_ids`. Additional values should not be indicated until 186 /// this procedure completes. 187 virtual async2::OnceReceiver<ValueChangedResult> IndicateValue( 188 ValueChangedParameters&& parameters) = 0; 189 190 private: 191 /// Unpublish the local service. This method is called by the 192 /// ~LocalService::Ptr() when it goes out of scope, the API client should 193 /// never call this method. 194 virtual void UnpublishService() = 0; 195 196 public: 197 /// Movable LocalService smart pointer. When the LocalService::Ptr object is 198 /// destroyed the service will be unpublished. 199 using Ptr = 200 internal::RaiiPtr<LocalService2, &LocalService2::UnpublishService>; 201 }; 202 203 /// Interface for a GATT server that serves many GATT services. 204 class Server2 { 205 public: 206 enum class PublishServiceError { 207 kInternalError = 0, 208 209 /// The service handle provided was not unique. 210 kInvalidHandle = 1, 211 212 /// Invalid service UUID provided. 213 kInvalidUuid = 2, 214 215 /// Invalid service characteristics provided. 216 kInvalidCharacteristics = 3, 217 218 /// Invalid service includes provided. 219 kInvalidIncludes = 4, 220 }; 221 222 /// Parameters for registering a local GATT service. 223 struct LocalServiceInfo { 224 /// A unique (within a Server) handle identifying this service. 225 ServiceHandle handle; 226 227 /// Indicates whether this is a primary or secondary service. 228 bool primary; 229 230 /// The UUID that identifies the type of this service. 231 /// There may be multiple services with the same UUID. 232 Uuid type; 233 234 /// The characteristics of this service. 235 span<const Characteristic2> characteristics; 236 237 /// Handles of other services that are included by this service. The 238 /// included services need to already be published. 239 span<const ServiceHandle> includes; 240 }; 241 242 /// The Result passed by PublishService. 243 using PublishServiceResult = 244 pw::expected<LocalService2::Ptr, PublishServiceError>; 245 246 virtual ~Server2() = default; 247 248 /// Publishes the service defined by `info` and implemented by `delegate` so 249 /// that it is available to all remote peers. 250 /// 251 /// The caller must assign distinct handles to the characteristics and 252 /// descriptors listed in `info` per call to `PublishService` (handles can be 253 /// reused across calls). These identifiers will be used in requests sent to 254 /// `delegate`. 255 /// 256 /// @return On success, a `LocalService::Ptr` is returned via 257 /// `result_sender`. When the `LocalService::Ptr` is destroyed or an error 258 /// occurs (LocalServiceDelegate.OnError), the service will be unpublished. 259 virtual async2::OnceReceiver<PublishServiceResult> PublishService( 260 const LocalServiceInfo& info, LocalServiceDelegate2& delegate) = 0; 261 }; 262 263 } // namespace pw::bluetooth::gatt 264