• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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