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/internal/host/iso/iso_stream_manager.h"
16
17 #include "pw_bluetooth_sapphire/internal/host/hci-spec/protocol.h"
18 #include "pw_bluetooth_sapphire/internal/host/transport/control_packets.h"
19
20 namespace bt::iso {
21
IsoStreamManager(hci_spec::ConnectionHandle handle,hci::Transport::WeakPtr hci)22 IsoStreamManager::IsoStreamManager(hci_spec::ConnectionHandle handle,
23 hci::Transport::WeakPtr hci)
24 : acl_handle_(handle), hci_(std::move(hci)), weak_self_(this) {
25 PW_CHECK(hci_.is_alive());
26 cmd_ = hci_->command_channel()->AsWeakPtr();
27 PW_CHECK(cmd_.is_alive());
28
29 auto weak_self = GetWeakPtr();
30 cis_request_handler_ = cmd_->AddLEMetaEventHandler(
31 hci_spec::kLECISRequestSubeventCode,
32 [self = weak_self](const hci::EventPacket& event) {
33 if (!self.is_alive()) {
34 return hci::CommandChannel::EventCallbackResult::kRemove;
35 }
36 self->OnCisRequest(event);
37 return hci::CommandChannel::EventCallbackResult::kContinue;
38 });
39
40 disconnect_handler_ = cmd_->AddEventHandler(
41 hci_spec::kDisconnectionCompleteEventCode,
42 [self = std::move(weak_self)](const hci::EventPacket& event) {
43 if (!self.is_alive()) {
44 return hci::CommandChannel::EventCallbackResult::kRemove;
45 }
46 self->OnDisconnect(event);
47 return hci::CommandChannel::EventCallbackResult::kContinue;
48 });
49 }
50
~IsoStreamManager()51 IsoStreamManager::~IsoStreamManager() {
52 if (cmd_.is_alive()) {
53 cmd_->RemoveEventHandler(cis_request_handler_);
54 cmd_->RemoveEventHandler(disconnect_handler_);
55 }
56 if (hci_.is_alive()) {
57 hci::IsoDataChannel* iso_data_channel = hci_->iso_data_channel();
58 if (iso_data_channel) {
59 for (const auto& stream : streams_) {
60 hci_spec::ConnectionHandle cis_handle = stream.second->cis_handle();
61 bt_log(INFO,
62 "iso",
63 "unregistering ISO connection for handle %#x",
64 cis_handle);
65 iso_data_channel->UnregisterConnection(cis_handle);
66 }
67 }
68 }
69 }
70
AcceptCis(CigCisIdentifier id,CisEstablishedCallback cb)71 AcceptCisStatus IsoStreamManager::AcceptCis(CigCisIdentifier id,
72 CisEstablishedCallback cb) {
73 bt_log(INFO,
74 "iso",
75 "IsoStreamManager: preparing to accept incoming connection (CIG: %u, "
76 "CIS: %u)",
77 id.cig_id(),
78 id.cis_id());
79
80 if (accept_handlers_.count(id) != 0) {
81 return AcceptCisStatus::kAlreadyExists;
82 }
83
84 if (streams_.count(id) != 0) {
85 return AcceptCisStatus::kAlreadyExists;
86 }
87
88 accept_handlers_[id] = std::move(cb);
89 return AcceptCisStatus::kSuccess;
90 }
91
OnCisRequest(const hci::EventPacket & event)92 void IsoStreamManager::OnCisRequest(const hci::EventPacket& event) {
93 PW_CHECK(event.event_code() == hci_spec::kLEMetaEventCode);
94
95 auto event_view =
96 event.view<pw::bluetooth::emboss::LECISRequestSubeventView>();
97 PW_CHECK(event_view.le_meta_event().subevent_code().Read() ==
98 hci_spec::kLECISRequestSubeventCode);
99
100 hci_spec::ConnectionHandle request_handle =
101 event_view.acl_connection_handle().Read();
102 uint8_t cig_id = event_view.cig_id().Read();
103 uint8_t cis_id = event_view.cis_id().Read();
104 CigCisIdentifier id(cig_id, cis_id);
105
106 bt_log(INFO,
107 "iso",
108 "CIS request received for handle 0x%x (CIG: %u, CIS: %u)",
109 request_handle,
110 cig_id,
111 cis_id);
112
113 // Ignore any requests that are not intended for this connection.
114 if (request_handle != acl_handle_) {
115 bt_log(DEBUG,
116 "iso",
117 "ignoring incoming stream request for handle 0x%x (ours: 0x%x)",
118 request_handle,
119 acl_handle_);
120 return;
121 }
122
123 // If we are not waiting on this request, reject it
124 if (accept_handlers_.count(id) == 0) {
125 bt_log(INFO, "iso", "Rejecting incoming request");
126 RejectCisRequest(event_view);
127 return;
128 }
129
130 bt_log(INFO, "iso", "Accepting incoming request");
131
132 // We should not already have an established stream using this same CIG/CIS
133 // permutation.
134 PW_CHECK(streams_.count(id) == 0, "(cig = %u, cis = %u)", cig_id, cis_id);
135 CisEstablishedCallback cb = std::move(accept_handlers_[id]);
136 accept_handlers_.erase(id);
137 AcceptCisRequest(event_view, std::move(cb));
138 }
139
OnDisconnect(const hci::EventPacket & event)140 void IsoStreamManager::OnDisconnect(const hci::EventPacket& event) {
141 PW_CHECK(event.event_code() == hci_spec::kDisconnectionCompleteEventCode);
142 auto event_view =
143 event.view<pw::bluetooth::emboss::DisconnectionCompleteEventView>();
144 hci_spec::ConnectionHandle disconnected_handle =
145 event_view.connection_handle().Read();
146 for (auto it = streams_.begin(); it != streams_.end(); ++it) {
147 if (it->second->cis_handle() == disconnected_handle) {
148 bt_log(
149 INFO, "iso", "CIS Disconnected at handle %#x", disconnected_handle);
150 if (hci_.is_alive()) {
151 hci::IsoDataChannel* iso_data_channel = hci_->iso_data_channel();
152 if (iso_data_channel) {
153 iso_data_channel->UnregisterConnection(disconnected_handle);
154 }
155 }
156 streams_.erase(it);
157 // There shouldn't be any more, connections are unique.
158 return;
159 }
160 }
161 }
162
AcceptCisRequest(const pw::bluetooth::emboss::LECISRequestSubeventView & event_view,CisEstablishedCallback cb)163 void IsoStreamManager::AcceptCisRequest(
164 const pw::bluetooth::emboss::LECISRequestSubeventView& event_view,
165 CisEstablishedCallback cb) {
166 uint8_t cig_id = event_view.cig_id().Read();
167 uint8_t cis_id = event_view.cis_id().Read();
168 CigCisIdentifier id(cig_id, cis_id);
169
170 hci_spec::ConnectionHandle cis_handle =
171 event_view.cis_connection_handle().Read();
172
173 PW_CHECK(streams_.count(id) == 0);
174
175 auto on_closed_cb = [this, id, cis_handle]() {
176 if (hci_.is_alive()) {
177 hci::IsoDataChannel* iso_data_channel = hci_->iso_data_channel();
178 if (iso_data_channel) {
179 bt_log(INFO,
180 "iso",
181 "unregistering ISO connection for handle %#x",
182 cis_handle);
183 iso_data_channel->UnregisterConnection(cis_handle);
184 }
185 }
186 streams_.erase(id);
187 };
188
189 streams_[id] = IsoStream::Create(cig_id,
190 cis_id,
191 cis_handle,
192 std::move(cb),
193 cmd_->AsWeakPtr(),
194 on_closed_cb);
195
196 auto command = hci::CommandPacket::New<
197 pw::bluetooth::emboss::LEAcceptCISRequestCommandWriter>(
198 hci_spec::kLEAcceptCISRequest);
199 auto cmd_view = command.view_t();
200 cmd_view.connection_handle().Write(cis_handle);
201
202 auto self = GetWeakPtr();
203 auto cmd_complete_cb = [cis_handle, id, self](auto,
204 const hci::EventPacket& event) {
205 bt_log(INFO, "iso", "LE_Accept_CIS_Request command response received");
206 if (!self.is_alive()) {
207 return;
208 }
209 if (HCI_IS_ERROR(event,
210 WARN,
211 "bt-iso",
212 "accept CIS request failed for handle %#x",
213 cis_handle)) {
214 self->streams_.erase(id);
215 return;
216 }
217 hci::IsoDataChannel* iso_data_channel = self->hci_->iso_data_channel();
218 iso_data_channel->RegisterConnection(cis_handle,
219 self->streams_[id]->GetWeakPtr());
220 };
221
222 cmd_->SendCommand(std::move(command), cmd_complete_cb);
223 }
224
RejectCisRequest(const pw::bluetooth::emboss::LECISRequestSubeventView & event_view)225 void IsoStreamManager::RejectCisRequest(
226 const pw::bluetooth::emboss::LECISRequestSubeventView& event_view) {
227 hci_spec::ConnectionHandle cis_handle =
228 event_view.cis_connection_handle().Read();
229
230 auto command = hci::CommandPacket::New<
231 pw::bluetooth::emboss::LERejectCISRequestCommandWriter>(
232 hci_spec::kLERejectCISRequest);
233 auto cmd_view = command.view_t();
234 cmd_view.connection_handle().Write(cis_handle);
235 cmd_view.reason().Write(pw::bluetooth::emboss::StatusCode::UNSPECIFIED_ERROR);
236
237 cmd_->SendCommand(std::move(command),
238 [cis_handle](auto, const hci::EventPacket& event) {
239 bt_log(INFO, "iso", "LE_Reject_CIS_Request command sent");
240 HCI_IS_ERROR(event,
241 ERROR,
242 "bt-iso",
243 "reject CIS request failed for handle %#x",
244 cis_handle);
245 });
246 }
247
248 } // namespace bt::iso
249