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