• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #include "pw_bluetooth_sapphire/internal/host/gap/generic_access_client.h"
16 
17 #include <pw_assert/check.h>
18 #include <pw_bytes/endian.h>
19 
20 #include "pw_bluetooth_sapphire/internal/host/gap/gap.h"
21 
22 namespace bt::gap::internal {
23 
GenericAccessClient(PeerId peer_id,gatt::RemoteService::WeakPtr service)24 GenericAccessClient::GenericAccessClient(PeerId peer_id,
25                                          gatt::RemoteService::WeakPtr service)
26     : WeakSelf(this), service_(std::move(service)), peer_id_(peer_id) {
27   PW_CHECK(service_.is_alive());
28   PW_CHECK(service_->uuid() == kGenericAccessService);
29 }
30 
ReadDeviceName(DeviceNameCallback callback)31 void GenericAccessClient::ReadDeviceName(DeviceNameCallback callback) {
32   service_->DiscoverCharacteristics(
33       [self = GetWeakPtr(), cb = std::move(callback)](
34           att::Result<> result, const gatt::CharacteristicMap& chars) mutable {
35         if (!self.is_alive()) {
36           return;
37         }
38 
39         if (result.is_error()) {
40           cb(result.take_error());
41           return;
42         }
43 
44         std::optional<gatt::CharacteristicHandle> device_name_value_handle;
45         for (auto& [handle, chr] : chars) {
46           auto& data = chr.first;
47           if (data.type == kDeviceNameCharacteristic) {
48             device_name_value_handle.emplace(data.value_handle);
49             break;
50           }
51         }
52 
53         if (!device_name_value_handle) {
54           bt_log(DEBUG,
55                  "gap-le",
56                  "GAP service does not have device name characteristic "
57                  "(peer: %s)",
58                  bt_str(self->peer_id_));
59           cb(ToResult(HostError::kNotFound).take_error());
60           return;
61         }
62 
63         // according to Core Spec v5.3, Vol 3, Part C, 12.1: "0 to 248 octets in
64         // length"
65         self->service_->ReadLongCharacteristic(
66             *device_name_value_handle,
67             /*offset=*/0,
68             att::kMaxAttributeValueLength,
69             [self, device_name_cb = std::move(cb)](
70                 att::Result<> discover_result,
71                 const ByteBuffer& buffer,
72                 bool /*maybe_truncated*/) mutable {
73               if (!self.is_alive()) {
74                 return;
75               }
76 
77               if (bt_is_error(
78                       discover_result,
79                       DEBUG,
80                       "gap-le",
81                       "error reading device name characteristic (peer: %s)",
82                       bt_str(self->peer_id_))) {
83                 device_name_cb(discover_result.take_error());
84                 return;
85               }
86 
87               const auto device_name_end =
88                   std::find(buffer.begin(), buffer.end(), '\0');
89               device_name_cb(
90                   fit::ok(std::string(buffer.begin(), device_name_end)));
91             });
92       });
93 }
94 
ReadAppearance(AppearanceCallback callback)95 void GenericAccessClient::ReadAppearance(AppearanceCallback callback) {
96   service_->DiscoverCharacteristics([self = GetWeakPtr(),
97                                      cb = std::move(callback)](
98                                         att::Result<> result,
99                                         const gatt::CharacteristicMap&
100                                             chars) mutable {
101     if (!self.is_alive()) {
102       return;
103     }
104 
105     if (result.is_error()) {
106       cb(result.take_error());
107       return;
108     }
109 
110     std::optional<gatt::CharacteristicHandle> appearance_value_handle;
111     for (auto& [handle, chr] : chars) {
112       auto& data = chr.first;
113       if (data.type == kAppearanceCharacteristic) {
114         appearance_value_handle.emplace(data.value_handle);
115         break;
116       }
117     }
118 
119     if (!appearance_value_handle) {
120       bt_log(DEBUG,
121              "gap-le",
122              "GAP service does not have appearance characteristic "
123              "(peer: %s)",
124              bt_str(self->peer_id_));
125       cb(ToResult(HostError::kNotFound).take_error());
126       return;
127     }
128 
129     // according to Core Spec v5.3, Vol 3, Part C, 12.2: "2 octets in length"
130     self->service_->ReadCharacteristic(
131         *appearance_value_handle,
132         [self, appearance_cb = std::move(cb)](
133             att::Result<> discover_result,
134             const ByteBuffer& buffer,
135             bool /*maybe_truncated*/) mutable {
136           if (!self.is_alive()) {
137             return;
138           }
139 
140           if (bt_is_error(discover_result,
141                           DEBUG,
142                           "gap-le",
143                           "error reading appearance characteristic (peer: %s)",
144                           bt_str(self->peer_id_))) {
145             appearance_cb(discover_result.take_error());
146             return;
147           }
148 
149           if (buffer.size() != sizeof(uint16_t)) {
150             bt_log(
151                 DEBUG,
152                 "gap-le",
153                 "appearance characteristic has invalid value size (peer: %s)",
154                 bt_str(self->peer_id_));
155             appearance_cb(ToResult(HostError::kPacketMalformed).take_error());
156             return;
157           }
158 
159           uint16_t char_value = pw::bytes::ConvertOrderFrom(
160               cpp20::endian::little, buffer.template To<uint16_t>());
161           appearance_cb(fit::ok(char_value));
162         });
163   });
164 }
165 
ReadPeripheralPreferredConnectionParameters(ConnectionParametersCallback callback)166 void GenericAccessClient::ReadPeripheralPreferredConnectionParameters(
167     ConnectionParametersCallback callback) {
168   service_->DiscoverCharacteristics([self = GetWeakPtr(),
169                                      cb = std::move(callback)](
170                                         att::Result<> result,
171                                         const gatt::CharacteristicMap&
172                                             chars) mutable {
173     if (!self.is_alive()) {
174       return;
175     }
176 
177     if (result.is_error()) {
178       cb(result.take_error());
179       return;
180     }
181 
182     std::optional<gatt::CharacteristicHandle> conn_params_value_handle;
183     for (auto& [handle, chr] : chars) {
184       auto& data = chr.first;
185       if (data.type == kPeripheralPreferredConnectionParametersCharacteristic) {
186         conn_params_value_handle.emplace(data.value_handle);
187         break;
188       }
189     }
190 
191     if (!conn_params_value_handle) {
192       bt_log(DEBUG,
193              "gap-le",
194              "GAP service does not have peripheral preferred connection "
195              "parameters characteristic "
196              "(peer: %s)",
197              bt_str(self->peer_id_));
198       cb(ToResult(HostError::kNotFound).take_error());
199       return;
200     }
201 
202     self->service_->ReadCharacteristic(
203         *conn_params_value_handle,
204         [self, connection_params_cb = std::move(cb)](
205             att::Result<> discover_result,
206             const ByteBuffer& buffer,
207             bool /*maybe_truncated*/) mutable {
208           if (!self.is_alive()) {
209             return;
210           }
211 
212           if (bt_is_error(discover_result,
213                           DEBUG,
214                           "gap-le",
215                           "error reading peripheral preferred connection "
216                           "parameters characteristic "
217                           "(peer: %s)",
218                           bt_str(self->peer_id_))) {
219             connection_params_cb(discover_result.take_error());
220             return;
221           }
222 
223           if (buffer.size() !=
224               sizeof(
225                   PeripheralPreferredConnectionParametersCharacteristicValue)) {
226             bt_log(DEBUG,
227                    "gap-le",
228                    "peripheral preferred connection parameters characteristic "
229                    "has invalid value size "
230                    "(peer: %s)",
231                    bt_str(self->peer_id_));
232             connection_params_cb(
233                 ToResult(HostError::kPacketMalformed).take_error());
234             return;
235           }
236 
237           auto char_value = buffer.template To<
238               PeripheralPreferredConnectionParametersCharacteristicValue>();
239           hci_spec::LEPreferredConnectionParameters params(
240               pw::bytes::ConvertOrderFrom(cpp20::endian::little,
241                                           char_value.min_interval),
242               pw::bytes::ConvertOrderFrom(cpp20::endian::little,
243                                           char_value.max_interval),
244               pw::bytes::ConvertOrderFrom(cpp20::endian::little,
245                                           char_value.max_latency),
246               pw::bytes::ConvertOrderFrom(cpp20::endian::little,
247                                           char_value.supervision_timeout));
248 
249           connection_params_cb(fit::ok(params));
250         });
251   });
252 }
253 
254 }  // namespace bt::gap::internal
255