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