• 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/channel_server.h"
16 
17 #include <pw_assert/check.h>
18 
19 #include <algorithm>
20 namespace fidlbt = fuchsia::bluetooth;
21 
22 namespace bthost {
23 
ChannelServer(fidl::InterfaceRequest<fidlbt::Channel> request,bt::l2cap::Channel::WeakPtr channel,fit::callback<void ()> closed_callback)24 ChannelServer::ChannelServer(fidl::InterfaceRequest<fidlbt::Channel> request,
25                              bt::l2cap::Channel::WeakPtr channel,
26                              fit::callback<void()> closed_callback)
27     : ServerBase(this, std::move(request)),
28       channel_(std::move(channel)),
29       closed_cb_(std::move(closed_callback)),
30       weak_self_(this) {
31   binding()->set_error_handler(
32       [this](zx_status_t /*status*/) { OnProtocolClosed(); });
33 }
34 
~ChannelServer()35 ChannelServer::~ChannelServer() {
36   if (state_ != State::kDeactivated) {
37     bt_log(TRACE, "fidl", "Deactivating channel %u in dtor", channel_->id());
38     Deactivate();
39   }
40 }
41 
Send(std::vector<::fuchsia::bluetooth::Packet> packets,SendCallback callback)42 void ChannelServer::Send(std::vector<::fuchsia::bluetooth::Packet> packets,
43                          SendCallback callback) {
44   for (auto& fidl_packet : packets) {
45     std::vector<uint8_t>& packet = fidl_packet.packet;
46     if (packet.size() > channel_->max_tx_sdu_size()) {
47       bt_log(TRACE,
48              "fidl",
49              "Dropping %zu bytes for channel %u as max TX SDU is %u ",
50              packet.size(),
51              channel_->id(),
52              channel_->max_tx_sdu_size());
53       continue;
54     }
55 
56     // TODO(fxbug.dev/349653544): Avoid making a copy of `packet`, possibly by
57     // making DynamicByteBuffer wrap a std::vector.
58     auto buffer =
59         std::make_unique<bt::DynamicByteBuffer>(bt::BufferView(packet));
60     bool write_success = channel_->Send(std::move(buffer));
61     if (!write_success) {
62       bt_log(TRACE,
63              "fidl",
64              "Failed to write %zu bytes to channel %u",
65              buffer->size(),
66              channel_->id());
67     }
68   }
69 
70   fidlbt::Channel_Send_Response response;
71   // NOLINTNEXTLINE(performance-move-const-arg)
72   callback(fidlbt::Channel_Send_Result::WithResponse(std::move(response)));
73 }
74 
Receive(ReceiveCallback callback)75 void ChannelServer::Receive(ReceiveCallback callback) {
76   if (receive_cb_) {
77     binding()->Close(ZX_ERR_BAD_STATE);
78     OnProtocolClosed();
79     return;
80   }
81   receive_cb_ = std::move(callback);
82   ServiceReceiveQueue();
83 }
84 
WatchChannelParameters(WatchChannelParametersCallback callback)85 void ChannelServer::WatchChannelParameters(
86     WatchChannelParametersCallback callback) {
87   PW_CHECK(
88       !pending_watch_channel_parameters_.has_value(),
89       "WatchChannelParameters called while there was already a pending call.");
90   pending_watch_channel_parameters_ = std::move(callback);
91 }
92 
handle_unknown_method(uint64_t ordinal,bool method_has_response)93 void ChannelServer::handle_unknown_method(uint64_t ordinal,
94                                           bool method_has_response) {
95   bt_log(WARN,
96          "fidl",
97          "ChannelServer: received unknown method (ordinal: %lu)",
98          ordinal);
99 }
100 
Activate()101 bool ChannelServer::Activate() {
102   PW_CHECK(state_ == State::kActivating);
103 
104   WeakPtr self = weak_self_.GetWeakPtr();
105   bt::l2cap::ChannelId channel_id = channel_->id();
106   bool activate_success = channel_->Activate(
107       [self, channel_id](bt::ByteBufferPtr rx_data) {
108         // Note: this lambda _may_ be invoked immediately for buffered packets.
109         if (self.is_alive()) {
110           self->OnChannelDataReceived(std::move(rx_data));
111         } else {
112           bt_log(
113               TRACE,
114               "fidl",
115               "Ignoring data received on destroyed server (channel_id=%#.4x)",
116               channel_id);
117         }
118       },
119       [self, channel_id] {
120         if (self.is_alive()) {
121           self->OnChannelClosed();
122         } else {
123           bt_log(
124               TRACE,
125               "fidl",
126               "Ignoring channel closure on destroyed server (channel_id=%#.4x)",
127               channel_id);
128         }
129       });
130   if (!activate_success) {
131     return false;
132   }
133 
134   state_ = State::kActivated;
135   return true;
136 }
137 
Deactivate()138 void ChannelServer::Deactivate() {
139   PW_CHECK(state_ != State::kDeactivated);
140   state_ = State::kDeactivating;
141 
142   if (!receive_queue_.empty()) {
143     bt_log(DEBUG,
144            "fidl",
145            "Dropping %zu packets from channel %u due to channel closure",
146            receive_queue_.size(),
147            channel_->id());
148     receive_queue_.clear();
149   }
150   channel_->Deactivate();
151   binding()->Close(ZX_ERR_CONNECTION_RESET);
152 
153   state_ = State::kDeactivated;
154 }
155 
OnChannelDataReceived(bt::ByteBufferPtr rx_data)156 void ChannelServer::OnChannelDataReceived(bt::ByteBufferPtr rx_data) {
157   // Note: kActivating is deliberately permitted, as ChannelImpl::Activate()
158   // will synchronously deliver any queued frames.
159   PW_CHECK(state_ != State::kDeactivated);
160   if (state_ == State::kDeactivating) {
161     bt_log(DEBUG,
162            "fidl",
163            "Ignoring %s for channel %u while deactivating",
164            __func__,
165            channel_->id());
166     return;
167   }
168 
169   PW_CHECK(rx_data);
170   if (rx_data->size() == 0) {
171     bt_log(
172         DEBUG, "fidl", "Ignoring empty rx_data for channel %u", channel_->id());
173     return;
174   }
175 
176   PW_CHECK(receive_queue_.size() <= receive_queue_max_frames_);
177   // On a full queue, we drop the oldest element, on the theory that newer data
178   // is more useful. This should be true, e.g., for real-time applications such
179   // as voice calls. In the future, we may want to make the drop-head vs.
180   // drop-tail choice configurable.
181   if (receive_queue_.size() == receive_queue_max_frames_) {
182     // TODO(fxbug.dev/42082614): Add a metric for number of dropped frames.
183     receive_queue_.pop_front();
184   }
185 
186   receive_queue_.push_back(std::move(rx_data));
187   ServiceReceiveQueue();
188 }
189 
OnChannelClosed()190 void ChannelServer::OnChannelClosed() {
191   if (state_ == State::kDeactivating) {
192     bt_log(DEBUG,
193            "fidl",
194            "Ignoring %s for channel %u while deactivating",
195            __func__,
196            channel_->id());
197     return;
198   }
199   PW_CHECK(state_ == State::kActivated);
200   DeactivateAndRequestDestruction();
201 }
202 
OnProtocolClosed()203 void ChannelServer::OnProtocolClosed() { DeactivateAndRequestDestruction(); }
204 
DeactivateAndRequestDestruction()205 void ChannelServer::DeactivateAndRequestDestruction() {
206   Deactivate();
207   // closed_cb_ is expected to destroy `this`, so move the callback first.
208   auto closed_cb = std::move(closed_cb_);
209   closed_cb();
210 }
211 
ServiceReceiveQueue()212 void ChannelServer::ServiceReceiveQueue() {
213   if (!receive_cb_ || receive_queue_.empty()) {
214     return;
215   }
216   std::vector<uint8_t> buffer = receive_queue_.front()->ToVector();
217   receive_queue_.pop_front();
218 
219   ::fuchsia::bluetooth::Channel_Receive_Response response(
220       {fidlbt::Packet{std::move(buffer)}});
221   receive_cb_(
222       fidlbt::Channel_Receive_Result::WithResponse(std::move(response)));
223   receive_cb_ = nullptr;
224 }
225 
Create(fidl::InterfaceRequest<fidlbt::Channel> request,bt::l2cap::Channel::WeakPtr channel,fit::callback<void ()> closed_callback)226 std::unique_ptr<ChannelServer> ChannelServer::Create(
227     fidl::InterfaceRequest<fidlbt::Channel> request,
228     bt::l2cap::Channel::WeakPtr channel,
229     fit::callback<void()> closed_callback) {
230   if (!channel.is_alive()) {
231     return nullptr;
232   }
233 
234   std::unique_ptr<ChannelServer> server(new ChannelServer(
235       std::move(request), std::move(channel), std::move(closed_callback)));
236 
237   if (!server->Activate()) {
238     return nullptr;
239   }
240   return server;
241 }
242 }  // namespace bthost
243