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