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 <lib/fit/function.h> 17 #include <lib/fit/result.h> 18 19 #include "pw_bluetooth_sapphire/internal/host/att/att.h" 20 #include "pw_bluetooth_sapphire/internal/host/att/bearer.h" 21 #include "pw_bluetooth_sapphire/internal/host/att/write_queue.h" 22 #include "pw_bluetooth_sapphire/internal/host/common/uuid.h" 23 #include "pw_bluetooth_sapphire/internal/host/gatt/gatt_defs.h" 24 25 namespace bt::gatt { 26 27 // Implements GATT client-role procedures. A client operates over a single ATT 28 // data bearer. Client objects are solely used to map GATT procedures to ATT 29 // protocol methods and do not maintain service state. 30 class Client { 31 public: 32 // Constructs a new Client. |bearer| must outlive this object. 33 static std::unique_ptr<Client> Create(att::Bearer::WeakPtr bearer); 34 35 virtual ~Client() = default; 36 37 // Returns a weak pointer to this Client. The weak pointer should be checked 38 // on the data bearer's thread only as Client can only be accessed on that 39 // thread. 40 using WeakPtr = WeakSelf<Client>::WeakPtr; 41 virtual Client::WeakPtr GetWeakPtr() = 0; 42 43 // Returns the current ATT MTU. 44 virtual uint16_t mtu() const = 0; 45 46 // Initiates an MTU exchange and adjusts the bearer's MTU as outlined in Core 47 // Spec v5.3 Vol. 3 Part F 3.4.2. The request will be made using the 48 // locally-preferred MTU. 49 // 50 // Upon successful exchange, the bearer will be updated to use the resulting 51 // MTU and |callback| will be notified with the the resulting MTU. 52 // 53 // MTU exchange support is optional per v5.3 Vol. 3 Part F Table 4.1, so if 54 // the exchange fails because the peer doesn't support it, the bearer's MTU 55 // will be |kLEMinMTU| and initialization should proceed. In this case, 56 // |mtu_result| will be |att::Error(att::kRequestNotSupported)|. 57 // 58 // If the MTU exchange otherwise fails, |mtu_result| will be an error and the 59 // bearer's MTU will not change. 60 using MTUCallback = fit::callback<void(att::Result<uint16_t> mtu_result)>; 61 virtual void ExchangeMTU(MTUCallback callback) = 0; 62 63 // Performs a modified version of the "Discover All Primary Services" 64 // procedure defined in v5.0, Vol 3, Part G, 4.4.1, genericized over primary 65 // and secondary services. 66 // 67 // |service_callback| is run for each discovered service in order of start 68 // handle. |status_callback| is run with the status of the operation. 69 // 70 // The |kind| parameter can be used to control whether primary or secondary 71 // services get discovered. 72 // 73 // NOTE: |service_callback| will be called asynchronously as services are 74 // discovered so a caller can start processing the results immediately while 75 // the procedure is in progress. Since discovery usually occurs over multiple 76 // ATT transactions, it is possible for |status_callback| to be called with an 77 // error even if some services have been discovered. It is up to the client 78 // to clear any cached state in this case. 79 using ServiceCallback = fit::function<void(const ServiceData&)>; 80 virtual void DiscoverServices(ServiceKind kind, 81 ServiceCallback svc_callback, 82 att::ResultFunction<> status_callback) = 0; 83 84 // Same as DiscoverServices, but only discovers services in the range 85 // [range_start, range_end]. |range_start| must be less than |range_end|. 86 virtual void DiscoverServicesInRange( 87 ServiceKind kind, 88 att::Handle range_start, 89 att::Handle range_end, 90 ServiceCallback svc_callback, 91 att::ResultFunction<> status_callback) = 0; 92 93 // Performs the "Discover All Primary Services by UUID" procedure defined in 94 // v5.0, Vol 3, Part G, 4.4.2. |service_callback| is run for each discovered 95 // service in order of start handle. |status_callback| is run with the result 96 // of the operation. 97 // 98 // The |kind| parameter can be used to control whether primary or secondary 99 // services get discovered. 100 // 101 // NOTE: |service_callback| will be called asynchronously as services are 102 // discovered so a caller can start processing the results immediately while 103 // the procedure is in progress. Since discovery usually occurs over multiple 104 // ATT transactions, it is possible for |status_callback| to be called with an 105 // error even if some services have been discovered. It is up to the client 106 // to clear any cached state in this case. 107 virtual void DiscoverServicesWithUuids(ServiceKind kind, 108 ServiceCallback svc_callback, 109 att::ResultFunction<> status_callback, 110 std::vector<UUID> uuids) = 0; 111 112 // Same as DiscoverServicesWithUuids, but only discovers services in the range 113 // [range_start, range_end]. |range_start| must be <= |range_end|. 114 virtual void DiscoverServicesWithUuidsInRange( 115 ServiceKind kind, 116 att::Handle range_start, 117 att::Handle range_end, 118 ServiceCallback svc_callback, 119 att::ResultFunction<> status_callback, 120 std::vector<UUID> uuids) = 0; 121 122 // Performs the "Discover All Characteristics of a Service" procedure defined 123 // in v5.0, Vol 3, Part G, 4.6.1. 124 using CharacteristicCallback = fit::function<void(const CharacteristicData&)>; 125 virtual void DiscoverCharacteristics( 126 att::Handle range_start, 127 att::Handle range_end, 128 CharacteristicCallback chrc_callback, 129 att::ResultFunction<> status_callback) = 0; 130 131 // Performs the "Discover All Characteristic Descriptors" procedure defined in 132 // Vol 3, Part G, 4.7.1. 133 using DescriptorCallback = fit::function<void(const DescriptorData&)>; 134 virtual void DiscoverDescriptors(att::Handle range_start, 135 att::Handle range_end, 136 DescriptorCallback desc_callback, 137 att::ResultFunction<> status_callback) = 0; 138 139 // Sends an ATT Read Request with the requested attribute |handle| and returns 140 // the resulting value in |callback|. This can be used to send a (short) read 141 // request to any attribute. (Vol 3, Part F, 3.4.4.3). 142 // 143 // Reports the status of the procedure and the resulting value in |callback|. 144 // Returns an empty buffer if the status is an error. 145 // If the attribute value might be longer than the reported value, 146 // |maybe_truncated| will be true. This can happen if the MTU is too small to 147 // read the complete value. ReadBlobRequest() should be used to read the 148 // complete value. 149 using ReadCallback = fit::function<void( 150 att::Result<>, const ByteBuffer&, bool maybe_truncated)>; 151 virtual void ReadRequest(att::Handle handle, ReadCallback callback) = 0; 152 153 // Sends an ATT Read by Type Request with the requested attribute handle range 154 // |start_handle| to |end_handle| (inclusive), and returns a ReadByTypeResult 155 // containing either the handle-value pairs successfully read, or an error 156 // status and related handle (if any). 157 // 158 // Attribute values may be truncated, as indicated by 159 // ReadByTypeValue.maybe_truncated. ReadRequest() or ReadBlobRequest() should 160 // be used to read the complete values. (Core Spec v5.2, Vol 3, Part 161 // F, 3.4.4.2) 162 // 163 // The attributes returned will be the attributes with the lowest handles in 164 // the handle range in ascending order, and may not include all matching 165 // attributes. To read all attributes, make additional requests with an 166 // updated |start_handle| until an error response with error code "Attribute 167 // Not Found" is received, or the entire handle range has been read. (Core 168 // Spec v5.2, Vol 3, Part F, 3.4.4.1). 169 struct ReadByTypeValue { 170 att::Handle handle; 171 // The underlying value buffer is only valid for the duration of |callback|. 172 // Callers must make a copy if they need to retain the buffer. 173 BufferView value; 174 // True if |value| might be truncated. 175 bool maybe_truncated; 176 }; 177 struct ReadByTypeError { 178 att::Error error; 179 // Only some att protocol errors include a handle. This handle can either be 180 // |start_handle| or the handle of the attribute causing the error, 181 // depending on the error (Core Spec v5.2, Vol 3, Part F, 3.4.4.1). 182 std::optional<att::Handle> handle; 183 }; 184 using ReadByTypeResult = 185 fit::result<ReadByTypeError, std::vector<ReadByTypeValue>>; 186 using ReadByTypeCallback = fit::function<void(ReadByTypeResult)>; 187 virtual void ReadByTypeRequest(const UUID& type, 188 att::Handle start_handle, 189 att::Handle end_handle, 190 ReadByTypeCallback callback) = 0; 191 192 // Sends an ATT Read Blob request with the requested attribute |handle| and 193 // returns the result value in |callback|. This can be called multiple times 194 // to read the value of a characteristic that is larger than (ATT_MTU - 1). 195 // If the attribute value might be longer than the reported value, the 196 // |maybe_truncated| callback parameter will be true. (Vol 3, Part G, 4.8.3) 197 virtual void ReadBlobRequest(att::Handle handle, 198 uint16_t offset, 199 ReadCallback callback) = 0; 200 201 // Sends an ATT Write Request with the requested attribute |handle| and 202 // |value|. This can be used to send a write request to any attribute. 203 // (Vol 3, Part F, 3.4.5.1). 204 // 205 // Reports the status of the procedure in |callback|. 206 // HostError::kPacketMalformed is returned if |value| is too large to write in 207 // a single ATT request. 208 virtual void WriteRequest(att::Handle handle, 209 const ByteBuffer& value, 210 att::ResultFunction<> callback) = 0; 211 212 // Adds a new PrepareWriteQueue to be sent. This sends multiple 213 // PrepareWriteRequests followed by an ExecuteWriteRequest. The request will 214 // be enqueued if there are any pending. 215 // 216 // Reports the status of the procedure in |callback|. 217 // HostError::kPacketMalformed is returned if any writes in the queue are too 218 // large to write in a single ATT request. 219 virtual void ExecutePrepareWrites(att::PrepareWriteQueue prep_write_queue, 220 ReliableMode reliable_mode, 221 att::ResultFunction<> callback) = 0; 222 223 // Sends an ATT Prepare Write Request with the requested attribute |handle|, 224 // |offset|, and |part_value|. This can be used to send a long write request 225 // to any attribute by following with 0-N prepare write requests and finally 226 // an Execute Write Request. 227 // (Vol 3, Part G, 4.9.4) 228 // 229 // Reports the status of the procedure in |callback|, along with mirroring the 230 // data written to the buffer. 231 // HostError::kPacketMalformed is returned if |part_value| is too large to 232 // write in a single ATT request. 233 using PrepareCallback = fit::function<void(att::Result<>, const ByteBuffer&)>; 234 virtual void PrepareWriteRequest(att::Handle handle, 235 uint16_t offset, 236 const ByteBuffer& part_value, 237 PrepareCallback callback) = 0; 238 239 // Following a series of Prepare Write Requests, this will write the series if 240 // the input is kWritePending, or cancel all pending if it is kCancelAll. 241 // (Vol 3, Part G, 4.9.4) 242 // 243 // Reports the status of the procedure in |callback|. 244 virtual void ExecuteWriteRequest(att::ExecuteWriteFlag flag, 245 att::ResultFunction<> callback) = 0; 246 247 // Sends an ATT Write Command with the requested |handle| and |value|. This 248 // should only be used with characteristics that support the "Write Without 249 // Response" property. 250 // 251 // Reports the status of the procedure in |callback|. 252 virtual void WriteWithoutResponse(att::Handle handle, 253 const ByteBuffer& value, 254 att::ResultFunction<> callback) = 0; 255 256 // Assigns a callback that will be called when a notification or indication 257 // PDU is received. 258 using NotificationCallback = fit::function<void(bool indication, 259 att::Handle handle, 260 const ByteBuffer& value, 261 bool maybe_truncated)>; 262 virtual void SetNotificationHandler(NotificationCallback handler) = 0; 263 }; 264 265 } // namespace bt::gatt 266