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
15 #include "pw_bluetooth_sapphire/fuchsia/host/fidl/low_energy_connection_server.h"
16
17 #include <fuchsia/bluetooth/cpp/fidl.h>
18 #include <pw_status/status.h>
19 #include <pw_status/try.h>
20
21 #include "pw_bluetooth_sapphire/fuchsia/host/fidl/helpers.h"
22 #include "pw_bluetooth_sapphire/internal/host/l2cap/l2cap_defs.h"
23 #include "pw_bluetooth_sapphire/internal/host/sm/types.h"
24
25 namespace fbt = fuchsia::bluetooth;
26 namespace fbg = fuchsia::bluetooth::gatt2;
27
28 namespace bthost {
29 namespace {
30
ConvertModeFromFidl(fbt::ChannelMode mode)31 pw::Result<bt::l2cap::CreditBasedFlowControlMode> ConvertModeFromFidl(
32 fbt::ChannelMode mode) {
33 switch (mode) {
34 case fbt::ChannelMode::LE_CREDIT_BASED_FLOW_CONTROL:
35 return bt::l2cap::CreditBasedFlowControlMode::kLeCreditBasedFlowControl;
36 case fbt::ChannelMode::ENHANCED_CREDIT_BASED_FLOW_CONTROL:
37 return bt::l2cap::CreditBasedFlowControlMode::
38 kEnhancedCreditBasedFlowControl;
39 default:
40 return pw::Status::Unimplemented();
41 }
42 }
43
ConvertParamsFromFidl(const fbt::ChannelParameters & fidl)44 pw::Result<bt::l2cap::ChannelParameters> ConvertParamsFromFidl(
45 const fbt::ChannelParameters& fidl) {
46 bt::l2cap::ChannelParameters params;
47
48 if (fidl.has_flush_timeout()) {
49 // Request included parameters that must not be set for an LE L2CAP channel.
50 return pw::Status::InvalidArgument();
51 }
52
53 if (fidl.has_channel_mode()) {
54 PW_TRY_ASSIGN(params.mode, ConvertModeFromFidl(fidl.channel_mode()));
55 } else {
56 params.mode =
57 bt::l2cap::CreditBasedFlowControlMode::kLeCreditBasedFlowControl;
58 }
59
60 if (fidl.has_max_rx_packet_size()) {
61 params.max_rx_sdu_size = fidl.max_rx_packet_size();
62 }
63
64 return params;
65 }
66
ConvertSecurityRequirementsFromFidl(const fbt::ChannelParameters & fidl)67 bt::sm::SecurityLevel ConvertSecurityRequirementsFromFidl(
68 const fbt::ChannelParameters& fidl) {
69 bt::sm::SecurityLevel security_level = bt::sm::SecurityLevel::kEncrypted;
70
71 if (!fidl.has_security_requirements()) {
72 return security_level;
73 }
74
75 auto& security_reqs = fidl.security_requirements();
76 if (security_reqs.has_authentication_required() &&
77 security_reqs.authentication_required()) {
78 security_level = bt::sm::SecurityLevel::kAuthenticated;
79 }
80 if (security_reqs.has_secure_connections_required() &&
81 security_reqs.secure_connections_required()) {
82 security_level = bt::sm::SecurityLevel::kSecureAuthenticated;
83 }
84 return security_level;
85 }
86
87 } // namespace
88
LowEnergyConnectionServer(bt::gap::Adapter::WeakPtr adapter,bt::gatt::GATT::WeakPtr gatt,std::unique_ptr<bt::gap::LowEnergyConnectionHandle> connection,zx::channel handle,fit::callback<void ()> closed_cb)89 LowEnergyConnectionServer::LowEnergyConnectionServer(
90 bt::gap::Adapter::WeakPtr adapter,
91 bt::gatt::GATT::WeakPtr gatt,
92 std::unique_ptr<bt::gap::LowEnergyConnectionHandle> connection,
93 zx::channel handle,
94 fit::callback<void()> closed_cb)
95 : ServerBase(this, std::move(handle)),
96 conn_(std::move(connection)),
97 closed_handler_(std::move(closed_cb)),
98 peer_id_(conn_->peer_identifier()),
99 adapter_(std::move(adapter)),
100 gatt_(std::move(gatt)),
101 weak_self_(this) {
102 PW_DCHECK(conn_);
103
104 set_error_handler([this](zx_status_t) { OnClosed(); });
105 conn_->set_closed_callback(
106 fit::bind_member<&LowEnergyConnectionServer::OnClosed>(this));
107 }
108
OnClosed()109 void LowEnergyConnectionServer::OnClosed() {
110 if (closed_handler_) {
111 binding()->Close(ZX_ERR_CONNECTION_RESET);
112 closed_handler_();
113 }
114 }
115
RequestGattClient(fidl::InterfaceRequest<fbg::Client> client)116 void LowEnergyConnectionServer::RequestGattClient(
117 fidl::InterfaceRequest<fbg::Client> client) {
118 if (gatt_client_server_.has_value()) {
119 bt_log(INFO,
120 "fidl",
121 "%s: gatt client server already bound (peer: %s)",
122 __FUNCTION__,
123 bt_str(peer_id_));
124 client.Close(ZX_ERR_ALREADY_BOUND);
125 return;
126 }
127
128 fit::callback<void()> server_error_cb = [this] {
129 bt_log(
130 TRACE, "fidl", "gatt client server error (peer: %s)", bt_str(peer_id_));
131 gatt_client_server_.reset();
132 };
133 gatt_client_server_.emplace(
134 peer_id_, gatt_, std::move(client), std::move(server_error_cb));
135 }
136
AcceptCis(fuchsia::bluetooth::le::ConnectionAcceptCisRequest parameters)137 void LowEnergyConnectionServer::AcceptCis(
138 fuchsia::bluetooth::le::ConnectionAcceptCisRequest parameters) {
139 if (!parameters.has_connection_stream()) {
140 bt_log(WARN, "fidl", "AcceptCis invoked without a connection stream");
141 return;
142 }
143 ::fidl::InterfaceRequest<::fuchsia::bluetooth::le::IsochronousStream>*
144 connection_stream = parameters.mutable_connection_stream();
145 uint8_t cig_id = parameters.cig_id();
146 uint8_t cis_id = parameters.cis_id();
147 bt::iso::CigCisIdentifier id(cig_id, cis_id);
148
149 // Check for existing stream with same CIG/CIS combination
150 if (iso_streams_.count(id) != 0) {
151 bt_log(WARN,
152 "fidl",
153 "AcceptCis invoked with duplicate ID (CIG: %u, CIS: %u)",
154 cig_id,
155 cis_id);
156 connection_stream->Close(ZX_ERR_INVALID_ARGS);
157 return;
158 }
159 auto stream_server = std::make_unique<IsoStreamServer>(
160 std::move(*connection_stream), [id, this]() { iso_streams_.erase(id); });
161 auto weak_stream_server = stream_server->GetWeakPtr();
162 iso_streams_[id] = std::move(stream_server);
163
164 bt::iso::AcceptCisStatus result = conn_->AcceptCis(
165 id,
166 [weak_stream_server](
167 pw::bluetooth::emboss::StatusCode status,
168 std::optional<bt::iso::IsoStream::WeakPtr> weak_stream_ptr,
169 const std::optional<bt::iso::CisEstablishedParameters>&
170 connection_params) {
171 if (weak_stream_server.is_alive()) {
172 if (status == pw::bluetooth::emboss::StatusCode::SUCCESS) {
173 PW_CHECK(weak_stream_ptr.has_value());
174 PW_CHECK(connection_params.has_value());
175 weak_stream_server->OnStreamEstablished(*weak_stream_ptr,
176 *connection_params);
177 } else {
178 weak_stream_server->OnStreamEstablishmentFailed(status);
179 }
180 }
181 });
182
183 switch (result) {
184 case bt::iso::AcceptCisStatus::kSuccess:
185 bt_log(INFO,
186 "fidl",
187 "waiting for incoming CIS connection (CIG: %u, CIS: %u)",
188 cig_id,
189 cis_id);
190 return;
191 case bt::iso::AcceptCisStatus::kNotPeripheral:
192 bt_log(WARN,
193 "fidl",
194 "attempt to wait for incoming CIS on Central not allowed");
195 iso_streams_[id]->Close(ZX_ERR_NOT_SUPPORTED);
196 return;
197 case bt::iso::AcceptCisStatus::kAlreadyExists:
198 bt_log(WARN,
199 "fidl",
200 "redundant request to wait for incoming CIS (CIG: %u, CIS: %u)",
201 cig_id,
202 cis_id);
203 iso_streams_[id]->Close(ZX_ERR_INVALID_ARGS);
204 return;
205 default:
206 BT_PANIC("Invalid AcceptCisStatus value %d", static_cast<int>(result));
207 }
208 }
209
GetCodecLocalDelayRange(::fuchsia::bluetooth::le::CodecDelayGetCodecLocalDelayRangeRequest parameters,GetCodecLocalDelayRangeCallback callback)210 void LowEnergyConnectionServer::GetCodecLocalDelayRange(
211 ::fuchsia::bluetooth::le::CodecDelayGetCodecLocalDelayRangeRequest
212 parameters,
213 GetCodecLocalDelayRangeCallback callback) {
214 bt_log(INFO, "fidl", "request received to read controller supported delay");
215
216 if (!parameters.has_logical_transport_type()) {
217 bt_log(WARN,
218 "fidl",
219 "request to read controller delay missing logical_transport_type");
220 callback(fpromise::error(ZX_ERR_INVALID_ARGS));
221 return;
222 }
223
224 if (!parameters.has_data_direction()) {
225 bt_log(WARN,
226 "fidl",
227 "request to read controller delay missing data_direction");
228 callback(fpromise::error(ZX_ERR_INVALID_ARGS));
229 return;
230 }
231
232 if (!parameters.has_codec_attributes()) {
233 bt_log(WARN,
234 "fidl",
235 "request to read controller delay missing codec_attributes");
236 callback(fpromise::error(ZX_ERR_INVALID_ARGS));
237 return;
238 }
239
240 if (!parameters.codec_attributes().has_codec_id()) {
241 bt_log(WARN, "fidl", "request to read controller delay missing codec_id");
242 callback(fpromise::error(ZX_ERR_INVALID_ARGS));
243 return;
244 }
245
246 // Process required parameters
247 pw::bluetooth::emboss::LogicalTransportType transport_type =
248 fidl_helpers::LogicalTransportTypeFromFidl(
249 parameters.logical_transport_type());
250 pw::bluetooth::emboss::DataPathDirection direction =
251 fidl_helpers::DataPathDirectionFromFidl(parameters.data_direction());
252 bt::StaticPacket<pw::bluetooth::emboss::CodecIdWriter> codec_id =
253 fidl_helpers::CodecIdFromFidl(parameters.codec_attributes().codec_id());
254
255 // Codec configuration is optional
256 std::optional<std::vector<uint8_t>> codec_configuration;
257 if (parameters.codec_attributes().has_codec_configuration()) {
258 codec_configuration = parameters.codec_attributes().codec_configuration();
259 } else {
260 codec_configuration = std::nullopt;
261 }
262
263 adapter_->GetSupportedDelayRange(
264 codec_id,
265 transport_type,
266 direction,
267 codec_configuration,
268 [callback = std::move(callback)](
269 pw::Status status, uint32_t min_delay_us, uint32_t max_delay_us) {
270 if (!status.ok()) {
271 bt_log(WARN, "fidl", "failed to get controller supported delay");
272 callback(fpromise::error(ZX_ERR_INTERNAL));
273 return;
274 }
275 bt_log(INFO,
276 "fidl",
277 "controller supported delay [%d, %d] microseconds",
278 min_delay_us,
279 max_delay_us);
280 fuchsia::bluetooth::le::CodecDelay_GetCodecLocalDelayRange_Response
281 response;
282 zx::duration min_delay = zx::usec(min_delay_us);
283 zx::duration max_delay = zx::usec(max_delay_us);
284 response.set_min_controller_delay(min_delay.get());
285 response.set_max_controller_delay(max_delay.get());
286 callback(
287 fuchsia::bluetooth::le::CodecDelay_GetCodecLocalDelayRange_Result::
288 WithResponse(std::move(response)));
289 });
290 }
291
ConnectL2cap(fuchsia::bluetooth::le::ConnectionConnectL2capRequest request)292 void LowEnergyConnectionServer::ConnectL2cap(
293 fuchsia::bluetooth::le::ConnectionConnectL2capRequest request) {
294 // Make available in callback below.
295 constexpr auto kFuncName = __FUNCTION__;
296
297 if (!request.has_channel()) {
298 bt_log(WARN,
299 "fidl",
300 "%s: No channel request, cannot fulfill call.",
301 kFuncName);
302 return;
303 }
304
305 if (!request.has_psm()) {
306 bt_log(ERROR, "fidl", "%s: missing psm.", kFuncName);
307 request.mutable_channel()->Close(ZX_ERR_INVALID_ARGS);
308 return;
309 }
310
311 if (!request.has_parameters()) {
312 bt_log(DEBUG,
313 "fidl",
314 "%s: No parameters provided, using default parameters.",
315 kFuncName);
316 }
317
318 auto parameters = ConvertParamsFromFidl(*request.mutable_parameters());
319 if (!parameters.ok()) {
320 bt_log(ERROR, "fidl", "%s: Parameters invalid.", kFuncName);
321 request.mutable_channel()->Close(ZX_ERR_INVALID_ARGS);
322 return;
323 }
324
325 bt::sm::SecurityLevel security_level =
326 ConvertSecurityRequirementsFromFidl(*request.mutable_parameters());
327 auto cb = [self = weak_self_.GetWeakPtr(),
328 request = std::move(*request.mutable_channel())](
329 bt::l2cap::Channel::WeakPtr channel) mutable {
330 if (!self.is_alive()) {
331 bt_log(
332 WARN,
333 "fidl",
334 "%s (cb): Connection server was destroyed before callback completed.",
335 kFuncName);
336 request.Close(ZX_ERR_INTERNAL);
337 return;
338 }
339 self->ServeChannel(std::move(channel), std::move(request));
340 };
341
342 PW_CHECK(adapter_.is_alive());
343 adapter_->le()->OpenL2capChannel(
344 peer_id_, request.psm(), *parameters, security_level, std::move(cb));
345 }
346
ServeChannel(bt::l2cap::Channel::WeakPtr channel,fidl::InterfaceRequest<fuchsia::bluetooth::Channel> request)347 void LowEnergyConnectionServer::ServeChannel(
348 bt::l2cap::Channel::WeakPtr channel,
349 fidl::InterfaceRequest<fuchsia::bluetooth::Channel> request) {
350 if (!channel.is_alive()) {
351 bt_log(WARN,
352 "fidl",
353 "%s: Channel was destroyed before it could be served.",
354 __FUNCTION__);
355 request.Close(ZX_ERR_INTERNAL);
356 }
357
358 bt::l2cap::Channel::UniqueId unique_id = channel->unique_id();
359
360 auto on_close = [this, unique_id]() { channel_servers_.erase(unique_id); };
361
362 auto server = ChannelServer::Create(
363 std::move(request), std::move(channel), std::move(on_close));
364
365 if (!server) {
366 bt_log(ERROR,
367 "fidl",
368 "%s: Channel server could not be created.",
369 __FUNCTION__);
370 request.Close(ZX_ERR_INTERNAL);
371 return;
372 }
373
374 channel_servers_[unique_id] = std::move(server);
375 }
376
377 } // namespace bthost
378