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