• 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 "loopback.h"
16 
17 #include <lib/driver/logging/cpp/logger.h>
18 
19 namespace bt_hci_virtual {
20 
LoopbackDevice()21 LoopbackDevice::LoopbackDevice()
22     : dispatcher_(fdf::Dispatcher::GetCurrent()->async_dispatcher()),
23       devfs_connector_(fit::bind_member<&LoopbackDevice::Connect>(this)) {}
24 
Initialize(zx::channel channel,std::string_view name,AddChildCallback callback)25 zx_status_t LoopbackDevice::Initialize(zx::channel channel,
26                                        std::string_view name,
27                                        AddChildCallback callback) {
28   // Setup up incoming channel waiter
29   loopback_chan_ = std::move(channel);
30   loopback_chan_wait_.set_object(loopback_chan_.get());
31   loopback_chan_wait_.set_trigger(ZX_CHANNEL_READABLE | ZX_CHANNEL_PEER_CLOSED);
32   ZX_ASSERT(loopback_chan_wait_.Begin(dispatcher_) == ZX_OK);
33 
34   // Create args to add loopback as a child node on behalf of VirtualController
35   zx::result connector = devfs_connector_.Bind(dispatcher_);
36   if (connector.is_error()) {
37     FDF_LOG(ERROR,
38             "Failed to bind devfs connecter to dispatcher: %u",
39             connector.status_value());
40     return connector.error_value();
41   }
42 
43   fidl::Arena args_arena;
44   auto devfs = fuchsia_driver_framework::wire::DevfsAddArgs::Builder(args_arena)
45                    .connector(std::move(connector.value()))
46                    .class_name("bt-hci")
47                    .Build();
48   auto args = fuchsia_driver_framework::wire::NodeAddArgs::Builder(args_arena)
49                   .name(name.data())
50                   .devfs_args(devfs)
51                   .Build();
52 
53   callback(args);
54 
55   return ZX_OK;
56 }
57 
SnoopServer(fidl::ServerEnd<fuchsia_hardware_bluetooth::Snoop> server_end,LoopbackDevice * device)58 LoopbackDevice::SnoopServer::SnoopServer(
59     fidl::ServerEnd<fuchsia_hardware_bluetooth::Snoop> server_end,
60     LoopbackDevice* device)
61     : binding_(device->dispatcher_,
62                std::move(server_end),
63                this,
64                std::mem_fn(&LoopbackDevice::SnoopServer::OnFidlError)),
65       device_(device) {}
66 
QueueSnoopPacket(uint8_t * buffer,size_t length,PacketIndicator indicator,fuchsia_hardware_bluetooth::PacketDirection direction)67 void LoopbackDevice::SnoopServer::QueueSnoopPacket(
68     uint8_t* buffer,
69     size_t length,
70     PacketIndicator indicator,
71     fuchsia_hardware_bluetooth::PacketDirection direction) {
72   uint64_t sequence = next_sequence_++;
73 
74   if (sequence > acked_sequence_ + kMaxSnoopUnackedPackets) {
75     if (queued_packets_.size() >= kMaxSnoopQueueSize) {
76       // Drop oldest packet.
77       fuchsia_hardware_bluetooth::PacketDirection direction =
78           queued_packets_.front().direction;
79       queued_packets_.pop();
80       if (direction ==
81           fuchsia_hardware_bluetooth::PacketDirection::kHostToController) {
82         dropped_sent_++;
83       } else {
84         dropped_received_++;
85       }
86     }
87     std::vector<uint8_t> packet(buffer, buffer + length);
88     queued_packets_.push(
89         Packet{std::move(packet), sequence, indicator, direction});
90     return;
91   }
92 
93   SendSnoopPacket(buffer, length, indicator, direction, sequence);
94 }
95 
SendSnoopPacket(uint8_t * buffer,size_t length,PacketIndicator indicator,fuchsia_hardware_bluetooth::PacketDirection direction,uint64_t sequence)96 void LoopbackDevice::SnoopServer::SendSnoopPacket(
97     uint8_t* buffer,
98     size_t length,
99     PacketIndicator indicator,
100     fuchsia_hardware_bluetooth::PacketDirection direction,
101     uint64_t sequence) {
102   fidl::VectorView vec_view =
103       fidl::VectorView<uint8_t>::FromExternal(buffer, length);
104   fidl::ObjectView obj_view =
105       fidl::ObjectView<fidl::VectorView<uint8_t>>::FromExternal(&vec_view);
106   fuchsia_hardware_bluetooth::wire::SnoopPacket packet;
107   switch (indicator) {
108     case PacketIndicator::kHciCommand:
109       packet =
110           fuchsia_hardware_bluetooth::wire::SnoopPacket::WithCommand(obj_view);
111       break;
112     case PacketIndicator::kHciAclData:
113       packet = fuchsia_hardware_bluetooth::wire::SnoopPacket::WithAcl(obj_view);
114       break;
115     case PacketIndicator::kHciSco:
116       packet = fuchsia_hardware_bluetooth::wire::SnoopPacket::WithSco(obj_view);
117       break;
118     case PacketIndicator::kHciEvent:
119       packet =
120           fuchsia_hardware_bluetooth::wire::SnoopPacket::WithEvent(obj_view);
121       break;
122     case PacketIndicator::kHciIso:
123       packet = fuchsia_hardware_bluetooth::wire::SnoopPacket::WithIso(obj_view);
124       break;
125     default:
126       FDF_LOG(WARNING, "Unknown indicator in %s", __func__);
127       return;
128   }
129 
130   fidl::Arena arena;
131   fuchsia_hardware_bluetooth::wire::SnoopOnObservePacketRequest request =
132       fuchsia_hardware_bluetooth::wire::SnoopOnObservePacketRequest::Builder(
133           arena)
134           .sequence(sequence)
135           .direction(direction)
136           .packet(packet)
137           .Build();
138 
139   fidl::OneWayStatus observe_status =
140       fidl::WireSendEvent(binding_)->OnObservePacket(request);
141   if (!observe_status.ok()) {
142     FDF_LOG(WARNING,
143             "Failed to send OnObservePacket on Snoop: %s",
144             observe_status.status_string());
145   }
146 }
147 
AcknowledgePackets(AcknowledgePacketsRequest & request,AcknowledgePacketsCompleter::Sync & completer)148 void LoopbackDevice::SnoopServer::AcknowledgePackets(
149     AcknowledgePacketsRequest& request,
150     AcknowledgePacketsCompleter::Sync& completer) {
151   if (request.sequence() <= acked_sequence_) {
152     return;
153   }
154   acked_sequence_ = request.sequence();
155 
156   // Send Snoop.OnDroppedPackets if necessary before sending next
157   // Snoop.ObservePacket.
158   if (dropped_sent_ != 0 || dropped_received_ != 0) {
159     fuchsia_hardware_bluetooth::SnoopOnDroppedPacketsRequest request;
160     request.sent() = dropped_sent_;
161     request.received() = dropped_received_;
162     fit::result<fidl::OneWayError> result =
163         fidl::SendEvent(binding_)->OnDroppedPackets(request);
164     if (result.is_error()) {
165       FDF_LOG(WARNING,
166               "Failed to send Snoop.OnDroppedPackets event: %s",
167               result.error_value().status_string());
168     }
169     dropped_sent_ = 0;
170     dropped_received_ = 0;
171   }
172 
173   while (!queued_packets_.empty() &&
174          queued_packets_.front().sequence <=
175              acked_sequence_ + kMaxSnoopUnackedPackets) {
176     Packet& packet = queued_packets_.front();
177     SendSnoopPacket(packet.packet.data(),
178                     packet.packet.size(),
179                     packet.indicator,
180                     packet.direction,
181                     packet.sequence);
182     queued_packets_.pop();
183   }
184 }
185 
OnFidlError(fidl::UnbindInfo error)186 void LoopbackDevice::SnoopServer::OnFidlError(fidl::UnbindInfo error) {
187   if (!error.is_user_initiated()) {
188     FDF_LOG(INFO, "Snoop closed: %s", error.status_string());
189   }
190   device_->snoop_.reset();
191 }
192 
handle_unknown_method(fidl::UnknownMethodMetadata<fuchsia_hardware_bluetooth::Snoop> metadata,fidl::UnknownMethodCompleter::Sync & completer)193 void LoopbackDevice::SnoopServer::handle_unknown_method(
194     fidl::UnknownMethodMetadata<fuchsia_hardware_bluetooth::Snoop> metadata,
195     fidl::UnknownMethodCompleter::Sync& completer) {
196   FDF_LOG(WARNING, "Unknown Snoop method received");
197 }
198 
Connect(fidl::ServerEnd<fuchsia_hardware_bluetooth::Vendor> request)199 void LoopbackDevice::Connect(
200     fidl::ServerEnd<fuchsia_hardware_bluetooth::Vendor> request) {
201   vendor_binding_group_.AddBinding(
202       dispatcher_, std::move(request), this, fidl::kIgnoreBindingClosure);
203 }
204 
OnLoopbackChannelSignal(async_dispatcher_t * dispatcher,async::WaitBase * wait,zx_status_t status,const zx_packet_signal_t * signal)205 void LoopbackDevice::OnLoopbackChannelSignal(async_dispatcher_t* dispatcher,
206                                              async::WaitBase* wait,
207                                              zx_status_t status,
208                                              const zx_packet_signal_t* signal) {
209   if (status == ZX_ERR_CANCELED) {
210     return;
211   }
212   if (status != ZX_OK) {
213     FDF_LOG(
214         ERROR, "Loopback channel wait error: %s", zx_status_get_string(status));
215     return;
216   }
217 
218   if (signal->observed & ZX_CHANNEL_READABLE) {
219     ReadLoopbackChannel();
220   }
221 
222   if (signal->observed & ZX_CHANNEL_PEER_CLOSED) {
223     FDF_LOG(ERROR, "Loopback channel peer closed");
224     return;
225   }
226 
227   loopback_chan_wait_.Begin(dispatcher_);
228 }
229 
OnHciTransportUnbound(size_t binding_id,fidl::UnbindInfo info)230 void LoopbackDevice::OnHciTransportUnbound(size_t binding_id,
231                                            fidl::UnbindInfo info) {
232   hci_transport_servers_.erase(binding_id);
233 }
234 
ReadLoopbackChannel()235 void LoopbackDevice::ReadLoopbackChannel() {
236   while (true) {
237     uint32_t actual_bytes = 0;
238     zx_status_t read_status = loopback_chan_.read(
239         /*flags=*/0,
240         read_buffer_.data(),
241         /*handles=*/nullptr,
242         static_cast<uint32_t>(read_buffer_.size()),
243         /*num_handles=*/0,
244         &actual_bytes,
245         /*actual_handles=*/nullptr);
246 
247     if (read_status == ZX_ERR_SHOULD_WAIT) {
248       return;
249     }
250 
251     if (read_status != ZX_OK) {
252       FDF_LOG(ERROR,
253               "failed to read from loopback channel %s",
254               zx_status_get_string(read_status));
255       return;
256     }
257 
258     if (actual_bytes == 0) {
259       FDF_LOG(WARNING, "ignoring empty packet when reading loopback channel");
260       continue;
261     }
262 
263     for (auto& [_, server] : hci_transport_servers_) {
264       server.OnReceive(read_buffer_.data(), actual_bytes);
265     }
266 
267     if (snoop_.has_value()) {
268       snoop_->QueueSnoopPacket(
269           read_buffer_.data() + 1,
270           actual_bytes - 1,
271           static_cast<PacketIndicator>(read_buffer_[0]),
272           fuchsia_hardware_bluetooth::PacketDirection::kControllerToHost);
273     }
274   }
275 }
276 
WriteLoopbackChannel(PacketIndicator indicator,uint8_t * buffer,size_t length)277 void LoopbackDevice::WriteLoopbackChannel(PacketIndicator indicator,
278                                           uint8_t* buffer,
279                                           size_t length) {
280   // We tack on an indicator byte to the beginning of the payload.
281   // Use an iovec to avoid a large allocation + copy.
282   zx_channel_iovec_t iovs[2];
283   iovs[0] = {
284       .buffer = &indicator, .capacity = sizeof(indicator), .reserved = 0};
285   iovs[1] = {.buffer = buffer,
286              .capacity = static_cast<uint32_t>(length),
287              .reserved = 0};
288 
289   zx_status_t write_status =
290       loopback_chan_.write(/*flags=*/ZX_CHANNEL_WRITE_USE_IOVEC,
291                            /*bytes=*/iovs,
292                            /*num_bytes=*/2,
293                            /*handles=*/nullptr,
294                            /*num_handles=*/0);
295   if (write_status != ZX_OK) {
296     FDF_LOG(ERROR,
297             "Failed to write to loopback channel: %s",
298             zx_status_get_string(write_status));
299     return;
300   }
301 
302   if (snoop_.has_value()) {
303     snoop_->QueueSnoopPacket(
304         buffer,
305         length,
306         indicator,
307         fuchsia_hardware_bluetooth::PacketDirection::kHostToController);
308   }
309 }
310 
GetFeatures(GetFeaturesCompleter::Sync & completer)311 void LoopbackDevice::GetFeatures(GetFeaturesCompleter::Sync& completer) {
312   completer.Reply(fuchsia_hardware_bluetooth::VendorFeatures());
313 }
314 
EncodeCommand(EncodeCommandRequest & request,EncodeCommandCompleter::Sync & completer)315 void LoopbackDevice::EncodeCommand(EncodeCommandRequest& request,
316                                    EncodeCommandCompleter::Sync& completer) {
317   completer.Reply(fit::error(ZX_ERR_NOT_SUPPORTED));
318 }
319 
OpenHci(OpenHciCompleter::Sync & completer)320 void LoopbackDevice::OpenHci(OpenHciCompleter::Sync& completer) {
321   completer.Reply(fit::error(ZX_ERR_NOT_SUPPORTED));
322 }
323 
OpenHciTransport(OpenHciTransportCompleter::Sync & completer)324 void LoopbackDevice::OpenHciTransport(
325     OpenHciTransportCompleter::Sync& completer) {
326   auto endpoints =
327       fidl::CreateEndpoints<fuchsia_hardware_bluetooth::HciTransport>();
328   if (endpoints.is_error()) {
329     FDF_LOG(ERROR,
330             "Failed to create HciTransport endpoints: %s",
331             zx_status_get_string(endpoints.error_value()));
332     completer.Reply(fit::error(ZX_ERR_INTERNAL));
333     return;
334   }
335 
336   size_t binding_id = next_hci_transport_server_id_++;
337   auto [_, inserted] = hci_transport_servers_.try_emplace(
338       binding_id, this, binding_id, std::move(endpoints->server));
339   ZX_ASSERT(inserted);
340   completer.Reply(fit::ok(std::move(endpoints->client)));
341 }
342 
OpenSnoop(OpenSnoopCompleter::Sync & completer)343 void LoopbackDevice::OpenSnoop(OpenSnoopCompleter::Sync& completer) {
344   auto endpoints = fidl::CreateEndpoints<fuchsia_hardware_bluetooth::Snoop>();
345   if (endpoints.is_error()) {
346     FDF_LOG(ERROR,
347             "Failed to create Snoop endpoints: %s",
348             zx_status_get_string(endpoints.error_value()));
349     completer.Reply(fit::error(ZX_ERR_INTERNAL));
350     return;
351   }
352   snoop_.emplace(std::move(std::move(endpoints->server)), this);
353   completer.Reply(fit::ok(std::move(endpoints->client)));
354 }
355 
handle_unknown_method(fidl::UnknownMethodMetadata<fuchsia_hardware_bluetooth::Vendor> metadata,fidl::UnknownMethodCompleter::Sync & completer)356 void LoopbackDevice::handle_unknown_method(
357     fidl::UnknownMethodMetadata<fuchsia_hardware_bluetooth::Vendor> metadata,
358     fidl::UnknownMethodCompleter::Sync& completer) {
359   FDF_LOG(
360       ERROR,
361       "Unknown method in Vendor request, closing with ZX_ERR_NOT_SUPPORTED");
362   completer.Close(ZX_ERR_NOT_SUPPORTED);
363 }
364 
HciTransportServer(LoopbackDevice * device,size_t binding_id,fidl::ServerEnd<fuchsia_hardware_bluetooth::HciTransport> server_end)365 LoopbackDevice::HciTransportServer::HciTransportServer(
366     LoopbackDevice* device,
367     size_t binding_id,
368     fidl::ServerEnd<fuchsia_hardware_bluetooth::HciTransport> server_end)
369     : device_(device),
370       binding_id_(binding_id),
371       binding_(device_->dispatcher_,
372                std::move(server_end),
373                this,
374                std::mem_fn(&HciTransportServer::OnUnbound)) {}
375 
OnReceive(uint8_t * buffer,size_t length)376 void LoopbackDevice::HciTransportServer::OnReceive(uint8_t* buffer,
377                                                    size_t length) {
378   if (receive_credits_ == 0 || !receive_queue_.empty()) {
379     if (receive_queue_.size() == kMaxReceiveQueueSize) {
380       FDF_LOG(ERROR, "Receive queue reached max size, dropping packet");
381       return;
382     }
383     std::vector<uint8_t> packet(buffer, buffer + length);
384     receive_queue_.push(std::move(packet));
385     return;
386   }
387   SendOnReceive(buffer, length);
388 }
389 
OnUnbound(fidl::UnbindInfo info)390 void LoopbackDevice::HciTransportServer::OnUnbound(fidl::UnbindInfo info) {
391   device_->hci_transport_servers_.erase(binding_id_);
392 }
393 
SendOnReceive(uint8_t * buffer,size_t length)394 void LoopbackDevice::HciTransportServer::SendOnReceive(uint8_t* buffer,
395                                                        size_t length) {
396   ZX_ASSERT(receive_credits_ != 0);
397   receive_credits_--;
398 
399   // Omit the indicator byte in ReceivedPacket.
400   fidl::VectorView vec_view =
401       fidl::VectorView<uint8_t>::FromExternal(buffer + 1, length - 1);
402   fidl::ObjectView obj_view =
403       fidl::ObjectView<fidl::VectorView<uint8_t>>::FromExternal(&vec_view);
404   fuchsia_hardware_bluetooth::wire::ReceivedPacket packet;
405   switch (static_cast<PacketIndicator>(buffer[0])) {
406     case PacketIndicator::kHciEvent:
407       packet =
408           fuchsia_hardware_bluetooth::wire::ReceivedPacket::WithEvent(obj_view);
409       break;
410     case PacketIndicator::kHciAclData:
411       packet =
412           fuchsia_hardware_bluetooth::wire::ReceivedPacket::WithAcl(obj_view);
413       break;
414     case PacketIndicator::kHciIso:
415       packet =
416           fuchsia_hardware_bluetooth::wire::ReceivedPacket::WithIso(obj_view);
417       break;
418     default:
419       FDF_LOG(WARNING, "Received invalid packet indicator on loopback channel");
420       return;
421   }
422   fidl::OneWayStatus status = fidl::WireSendEvent(binding_)->OnReceive(packet);
423   if (!status.ok()) {
424     FDF_LOG(
425         WARNING, "Error sending OnReceive event: %s", status.status_string());
426   }
427 }
428 
MaybeSendQueuedReceivePackets()429 void LoopbackDevice::HciTransportServer::MaybeSendQueuedReceivePackets() {
430   while (receive_credits_ != 0 && !receive_queue_.empty()) {
431     SendOnReceive(receive_queue_.front().data(), receive_queue_.front().size());
432     receive_queue_.pop();
433   }
434 }
435 
Send(SendRequest & request,SendCompleter::Sync & completer)436 void LoopbackDevice::HciTransportServer::Send(SendRequest& request,
437                                               SendCompleter::Sync& completer) {
438   completer.Reply();
439 
440   switch (request.Which()) {
441     case fuchsia_hardware_bluetooth::SentPacket::Tag::kCommand:
442       device_->WriteLoopbackChannel(PacketIndicator::kHciCommand,
443                                     request.command()->data(),
444                                     request.command()->size());
445       break;
446     case fuchsia_hardware_bluetooth::SentPacket::Tag::kAcl:
447       device_->WriteLoopbackChannel(PacketIndicator::kHciAclData,
448                                     request.acl()->data(),
449                                     request.acl()->size());
450       break;
451     case fuchsia_hardware_bluetooth::SentPacket::Tag::kIso:
452       device_->WriteLoopbackChannel(PacketIndicator::kHciIso,
453                                     request.iso()->data(),
454                                     request.iso()->size());
455       break;
456     default:
457       FDF_LOG(WARNING, "Unknown SentPacket type");
458   }
459 }
460 
AckReceive(AckReceiveCompleter::Sync & completer)461 void LoopbackDevice::HciTransportServer::AckReceive(
462     AckReceiveCompleter::Sync& completer) {
463   receive_credits_++;
464   MaybeSendQueuedReceivePackets();
465 }
466 
ConfigureSco(ConfigureScoRequest & request,ConfigureScoCompleter::Sync & completer)467 void LoopbackDevice::HciTransportServer::ConfigureSco(
468     ConfigureScoRequest& request, ConfigureScoCompleter::Sync& completer) {}
469 
handle_unknown_method(fidl::UnknownMethodMetadata<fuchsia_hardware_bluetooth::HciTransport> metadata,fidl::UnknownMethodCompleter::Sync & completer)470 void LoopbackDevice::HciTransportServer::handle_unknown_method(
471     fidl::UnknownMethodMetadata<fuchsia_hardware_bluetooth::HciTransport>
472         metadata,
473     fidl::UnknownMethodCompleter::Sync& completer) {
474   FDF_LOG(ERROR,
475           "Unknown method in HciTransport request, closing with "
476           "ZX_ERR_NOT_SUPPORTED");
477   completer.Close(ZX_ERR_NOT_SUPPORTED);
478 }
479 
480 }  // namespace bt_hci_virtual
481