• 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 #pragma once
16 
17 #include <fidl/fuchsia.driver.framework/cpp/fidl.h>
18 #include <fidl/fuchsia.hardware.bluetooth/cpp/fidl.h>
19 #include <lib/async-loop/cpp/loop.h>
20 #include <lib/driver/devfs/cpp/connector.h>
21 
22 #include <queue>
23 
24 #include "lib/async/cpp/wait.h"
25 
26 namespace bt_hci_virtual {
27 
28 using AddChildCallback =
29     fit::function<void(fuchsia_driver_framework::wire::NodeAddArgs)>;
30 
31 class LoopbackDevice : public fidl::Server<fuchsia_hardware_bluetooth::Vendor> {
32  public:
33   static constexpr size_t kMaxSnoopQueueSize = 20;
34   static constexpr size_t kMaxSnoopUnackedPackets = 10;
35   static constexpr size_t kMaxReceiveQueueSize = 40;
36   static constexpr size_t kMaxReceiveUnackedPackets = 10;
37 
38   explicit LoopbackDevice();
39 
40   // Methods to control the LoopbackDevice's lifecycle. These are used by the
41   // VirtualController. `channel` speaks the HCI UART protocol. `name` is the
42   // name to be used for the driver framework node. `callback` will be called
43   // with the NodeAddArgs when LoopbackDevice should be added as a child node.
44   zx_status_t Initialize(zx::channel channel,
45                          std::string_view name,
46                          AddChildCallback callback);
47 
48   // Called by driver_devfs::Connector.
49   void Connect(fidl::ServerEnd<fuchsia_hardware_bluetooth::Vendor> request);
50 
51  private:
52   // HCI UART packet indicators
53   enum class PacketIndicator : uint8_t {
54     kHciNone = 0,
55     kHciCommand = 1,
56     kHciAclData = 2,
57     kHciSco = 3,
58     kHciEvent = 4,
59     kHciIso = 5,
60   };
61 
62   class SnoopServer : public fidl::Server<fuchsia_hardware_bluetooth::Snoop> {
63    public:
64     SnoopServer(fidl::ServerEnd<fuchsia_hardware_bluetooth::Snoop> server_end,
65                 LoopbackDevice* device);
66 
67     // `buffer` should NOT have an indicator byte.
68     void QueueSnoopPacket(
69         uint8_t* buffer,
70         size_t length,
71         PacketIndicator indicator,
72         fuchsia_hardware_bluetooth::PacketDirection direction);
73 
74    private:
75     struct Packet {
76       std::vector<uint8_t> packet;
77       uint64_t sequence;
78       PacketIndicator indicator;
79       fuchsia_hardware_bluetooth::PacketDirection direction;
80     };
81 
82     void SendSnoopPacket(uint8_t* buffer,
83                          size_t length,
84                          PacketIndicator indicator,
85                          fuchsia_hardware_bluetooth::PacketDirection direction,
86                          uint64_t sequence);
87 
88     // fidl::Server<fuchsia_hardware_bluetooth::Snoop> overrides:
89     void AcknowledgePackets(
90         AcknowledgePacketsRequest& request,
91         AcknowledgePacketsCompleter::Sync& completer) override;
92     void handle_unknown_method(
93         fidl::UnknownMethodMetadata<fuchsia_hardware_bluetooth::Snoop> metadata,
94         fidl::UnknownMethodCompleter::Sync& completer) override;
95 
96     void OnFidlError(fidl::UnbindInfo error);
97 
98     fidl::ServerBinding<fuchsia_hardware_bluetooth::Snoop> binding_;
99     uint64_t next_sequence_ = 1u;
100     uint64_t acked_sequence_ = 0u;
101     uint32_t dropped_sent_ = 0u;
102     uint32_t dropped_received_ = 0u;
103     std::queue<Packet> queued_packets_;
104     LoopbackDevice* device_;
105   };
106 
107   class HciTransportServer
108       : public fidl::Server<fuchsia_hardware_bluetooth::HciTransport> {
109    public:
110     HciTransportServer(
111         LoopbackDevice* device,
112         size_t binding_id,
113         fidl::ServerEnd<fuchsia_hardware_bluetooth::HciTransport> server_end);
114 
115     // `buffer` must have an indicator byte.
116     void OnReceive(uint8_t* buffer, size_t length);
117 
118     void OnUnbound(fidl::UnbindInfo info);
119 
120    private:
121     void SendOnReceive(uint8_t* buffer, size_t length);
122     void MaybeSendQueuedReceivePackets();
123 
124     // fuchsia_hardware_bluetooth::HciTransport overrides:
125     void Send(SendRequest& request, SendCompleter::Sync& completer) override;
126     void AckReceive(AckReceiveCompleter::Sync& completer) override;
127     void ConfigureSco(ConfigureScoRequest& request,
128                       ConfigureScoCompleter::Sync& completer) override;
129     void handle_unknown_method(
130         fidl::UnknownMethodMetadata<fuchsia_hardware_bluetooth::HciTransport>
131             metadata,
132         fidl::UnknownMethodCompleter::Sync& completer) override;
133 
134     LoopbackDevice* device_;
135     size_t binding_id_;
136     size_t receive_credits_ = kMaxReceiveUnackedPackets;
137     std::queue<std::vector<uint8_t>> receive_queue_;
138     fidl::ServerBinding<fuchsia_hardware_bluetooth::HciTransport> binding_;
139   };
140 
141   void OnLoopbackChannelSignal(async_dispatcher_t* dispatcher,
142                                async::WaitBase* wait,
143                                zx_status_t status,
144                                const zx_packet_signal_t* signal);
145 
146   void OnHciTransportUnbound(size_t binding_id, fidl::UnbindInfo info);
147 
148   void ReadLoopbackChannel();
149   void WriteLoopbackChannel(PacketIndicator indicator,
150                             uint8_t* buffer,
151                             size_t length);
152 
153   // fuchsia_hardware_bluetooth::Vendor overrides:
154   void GetFeatures(GetFeaturesCompleter::Sync& completer) override;
155   void EncodeCommand(EncodeCommandRequest& request,
156                      EncodeCommandCompleter::Sync& completer) override;
157   void OpenHci(OpenHciCompleter::Sync& completer) override;
158   void OpenHciTransport(OpenHciTransportCompleter::Sync& completer) override;
159   void OpenSnoop(OpenSnoopCompleter::Sync& completer) override;
160   void handle_unknown_method(
161       fidl::UnknownMethodMetadata<fuchsia_hardware_bluetooth::Vendor> metadata,
162       fidl::UnknownMethodCompleter::Sync& completer) override;
163 
164   zx::channel loopback_chan_;
165   async::WaitMethod<LoopbackDevice, &LoopbackDevice::OnLoopbackChannelSignal>
166       loopback_chan_wait_{this};
167 
168   fidl::ServerBindingGroup<fuchsia_hardware_bluetooth::Vendor>
169       vendor_binding_group_;
170 
171   // Multiple HciTransport servers need to be supported: at least 1 for bt-host
172   // and 1 for bt-snoop.
173   std::unordered_map<size_t, HciTransportServer> hci_transport_servers_;
174   size_t next_hci_transport_server_id_ = 0u;
175 
176   // Only support 1 Snoop binding at a time.
177   std::optional<SnoopServer> snoop_;
178 
179   async_dispatcher_t* dispatcher_;
180 
181   // +1 for packet indicator
182   std::array<uint8_t, fuchsia_hardware_bluetooth::kAclPacketMax + 1>
183       read_buffer_;
184 
185   driver_devfs::Connector<fuchsia_hardware_bluetooth::Vendor> devfs_connector_;
186 };
187 
188 }  // namespace bt_hci_virtual
189