• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2023 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/internal/host/sco/sco_connection.h"
16 
17 namespace bt::sco {
18 
ScoConnection(std::unique_ptr<hci::Connection> connection,fit::closure deactivated_cb,bt::StaticPacket<pw::bluetooth::emboss::SynchronousConnectionParametersWriter> parameters,hci::ScoDataChannel * channel)19 ScoConnection::ScoConnection(
20     std::unique_ptr<hci::Connection> connection,
21     fit::closure deactivated_cb,
22     bt::StaticPacket<
23         pw::bluetooth::emboss::SynchronousConnectionParametersWriter>
24         parameters,
25     hci::ScoDataChannel* channel)
26     : active_(false),
27       connection_(std::move(connection)),
28       deactivated_cb_(std::move(deactivated_cb)),
29       channel_(channel),
30       parameters_(std::move(parameters)),
31       weak_self_(this) {
32   BT_ASSERT(connection_);
33   BT_ASSERT(!channel_ || channel_->max_data_length() <=
34                              hci_spec::kMaxSynchronousDataPacketPayloadSize);
35 
36   handle_ = connection_->handle();
37 
38   connection_->set_peer_disconnect_callback([this](const auto&, auto) {
39     // Notifies activator that this connection has been disconnected.
40     // Activator will call Deactivate().
41     Close();
42   });
43 }
44 
unique_id() const45 ScoConnection::UniqueId ScoConnection::unique_id() const {
46   // HCI connection handles are unique per controller.
47   return handle();
48 }
49 
id() const50 ScoConnection::UniqueId ScoConnection::id() const { return unique_id(); }
51 
Close()52 void ScoConnection::Close() {
53   bt_log(TRACE, "gap-sco", "closing sco connection (handle: %.4x)", handle());
54 
55   bool active = active_;
56   CleanUp();
57 
58   if (!active) {
59     return;
60   }
61 
62   BT_ASSERT(activator_closed_cb_);
63   // Move cb out of this, since cb may destroy this.
64   auto cb = std::move(activator_closed_cb_);
65   cb();
66 }
67 
Activate(fit::closure rx_callback,fit::closure closed_callback)68 bool ScoConnection::Activate(fit::closure rx_callback,
69                              fit::closure closed_callback) {
70   // TODO(fxbug.dev/42136417): Handle Activate() called on a connection that has
71   // been closed already.
72   BT_ASSERT(closed_callback);
73   BT_ASSERT(!active_);
74   BT_ASSERT(rx_callback);
75   activator_closed_cb_ = std::move(closed_callback);
76   rx_callback_ = std::move(rx_callback);
77   active_ = true;
78   if (channel_ && parameters_.view().input_data_path().Read() ==
79                       pw::bluetooth::emboss::ScoDataPath::HCI) {
80     channel_->RegisterConnection(GetWeakPtr());
81   }
82   return true;
83 }
84 
Deactivate()85 void ScoConnection::Deactivate() {
86   bt_log(
87       TRACE, "gap-sco", "deactivating sco connection (handle: %.4x)", handle());
88   CleanUp();
89   if (deactivated_cb_) {
90     // Move cb out of this, since cb may destroy this.
91     auto cb = std::move(deactivated_cb_);
92     cb();
93   }
94 }
95 
max_tx_sdu_size() const96 uint16_t ScoConnection::max_tx_sdu_size() const {
97   return channel_ ? channel_->max_data_length() : 0u;
98 }
99 
Send(ByteBufferPtr payload)100 bool ScoConnection::Send(ByteBufferPtr payload) {
101   if (!active_) {
102     bt_log(WARN,
103            "gap-sco",
104            "dropping SCO packet for inactive connection (handle: %#.4x)",
105            handle_);
106     return false;
107   }
108 
109   if (!channel_) {
110     bt_log(
111         WARN,
112         "gap-sco",
113         "dropping SCO packet because HCI SCO is not supported (handle: %#.4x)",
114         handle_);
115     return false;
116   }
117 
118   if (payload->size() > channel_->max_data_length()) {
119     bt_log(WARN,
120            "gap-sco",
121            "dropping SCO packet larger than the buffer data packet length "
122            "(packet size: %zu, max "
123            "data length: "
124            "%hu)",
125            payload->size(),
126            channel_->max_data_length());
127     return false;
128   }
129 
130   outbound_queue_.push(std::move(payload));
131 
132   // Notify ScoDataChannel that a packet is available. This is only necessary
133   // for the first packet of an empty queue (flow control will poll this
134   // connection otherwise).
135   if (outbound_queue_.size() == 1u) {
136     channel_->OnOutboundPacketReadable();
137   }
138   return true;
139 }
140 
Read()141 std::unique_ptr<hci::ScoDataPacket> ScoConnection::Read() {
142   if (inbound_queue_.empty()) {
143     return nullptr;
144   }
145   std::unique_ptr<hci::ScoDataPacket> packet =
146       std::move(inbound_queue_.front());
147   inbound_queue_.pop();
148   return packet;
149 }
150 
151 bt::StaticPacket<pw::bluetooth::emboss::SynchronousConnectionParametersWriter>
parameters()152 ScoConnection::parameters() {
153   return parameters_;
154 }
155 
GetNextOutboundPacket()156 std::unique_ptr<hci::ScoDataPacket> ScoConnection::GetNextOutboundPacket() {
157   if (outbound_queue_.empty()) {
158     return nullptr;
159   }
160 
161   std::unique_ptr<hci::ScoDataPacket> out = hci::ScoDataPacket::New(
162       handle(), static_cast<uint8_t>(outbound_queue_.front()->size()));
163   if (!out) {
164     bt_log(ERROR, "gap-sco", "failed to allocate SCO data packet");
165     return nullptr;
166   }
167   out->mutable_view()->mutable_payload_data().Write(
168       outbound_queue_.front()->view());
169   outbound_queue_.pop();
170   return out;
171 }
172 
ReceiveInboundPacket(std::unique_ptr<hci::ScoDataPacket> packet)173 void ScoConnection::ReceiveInboundPacket(
174     std::unique_ptr<hci::ScoDataPacket> packet) {
175   BT_ASSERT(packet->connection_handle() == handle_);
176 
177   if (!active_ || !rx_callback_) {
178     bt_log(TRACE, "gap-sco", "dropping inbound SCO packet");
179     return;
180   }
181 
182   inbound_queue_.push(std::move(packet));
183   // It's only necessary to notify activator of the first packet queued (flow
184   // control will poll this connection otherwise).
185   if (inbound_queue_.size() == 1u) {
186     rx_callback_();
187   }
188 }
189 
OnHciError()190 void ScoConnection::OnHciError() {
191   // Notify activator that this connection should be deactivated.
192   Close();
193 }
194 
CleanUp()195 void ScoConnection::CleanUp() {
196   if (active_ && channel_ &&
197       parameters_.view().input_data_path().Read() ==
198           pw::bluetooth::emboss::ScoDataPath::HCI) {
199     channel_->UnregisterConnection(handle_);
200   }
201   active_ = false;
202   connection_ = nullptr;
203 }
204 
205 }  // namespace bt::sco
206