xref: /aosp_15_r20/external/pigweed/pw_bluetooth_sapphire/host/iso/iso_stream_manager.cc (revision 61c4878ac05f98d0ceed94b57d316916de578985)
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