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 #pragma once 16 17 #include <cinttypes> 18 19 #include "pw_bluetooth_sapphire/internal/host/common/identifier.h" 20 #include "pw_bluetooth_sapphire/internal/host/common/weak_self.h" 21 #include "pw_bluetooth_sapphire/internal/host/hci-spec/protocol.h" 22 #include "pw_bluetooth_sapphire/internal/host/sco/sco_connection.h" 23 #include "pw_bluetooth_sapphire/internal/host/transport/transport.h" 24 25 namespace bt::sco { 26 27 // ScoConnectionManager handles SCO connections for a single BR/EDR connection. 28 // This includes queuing outbound and inbound connection requests and handling 29 // events related to SCO connections. 30 class ScoConnectionManager final { 31 public: 32 // Request handle returned to clients. Cancels request when destroyed. 33 class RequestHandle final { 34 public: RequestHandle(fit::callback<void ()> on_cancel)35 explicit RequestHandle(fit::callback<void()> on_cancel) 36 : on_cancel_(std::move(on_cancel)) {} 37 RequestHandle(RequestHandle&&) = default; 38 RequestHandle& operator=(RequestHandle&&) = default; ~RequestHandle()39 ~RequestHandle() { Cancel(); } 40 Cancel()41 void Cancel() { 42 if (on_cancel_) { 43 on_cancel_(); 44 } 45 } 46 47 private: 48 fit::callback<void()> on_cancel_; 49 BT_DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(RequestHandle); 50 }; 51 52 // |peer_id| corresponds to the peer associated with this BR/EDR connection. 53 // |acl_handle| corresponds to the ACL connection associated with these SCO 54 // connections. |transport| must outlive this object. 55 ScoConnectionManager(PeerId peer_id, 56 hci_spec::ConnectionHandle acl_handle, 57 DeviceAddress peer_address, 58 DeviceAddress local_address, 59 hci::Transport::WeakPtr transport); 60 // Closes connections and cancels connection requests. 61 ~ScoConnectionManager(); 62 63 // Initiate and outbound connection. A request will be queued if a connection 64 // is already in progress. On error, |callback| will be called with an error 65 // result. The error will be |kCanceled| if a connection was never attempted, 66 // or |kFailed| if establishing a connection failed. Returns a handle that 67 // will cancel the request when dropped (if connection establishment has not 68 // started). 69 using OpenConnectionResult = fit::result<HostError, ScoConnection::WeakPtr>; 70 using OpenConnectionCallback = fit::callback<void(OpenConnectionResult)>; 71 RequestHandle OpenConnection( 72 bt::StaticPacket< 73 pw::bluetooth::emboss::SynchronousConnectionParametersWriter> 74 parameters, 75 OpenConnectionCallback callback); 76 77 // Accept inbound connection requests using the parameters given in order. The 78 // parameters will be tried in order until either a connection is successful, 79 // all parameters have been rejected, or the procedure is canceled. On 80 // success, |callback| will be called with the connection object and the index 81 // of the parameters used to establish the connection. On error, |callback| 82 // will be called with an error result. If another Open/Accept request is made 83 // before a connection request is received, this request will be canceled 84 // (with error |kCanceled|). Returns a handle that will cancel the request 85 // when destroyed (if connection establishment has not started). 86 using AcceptConnectionResult = fit::result< 87 HostError, 88 std::pair<ScoConnection::WeakPtr, size_t /*index of parameters used*/>>; 89 using AcceptConnectionCallback = fit::callback<void(AcceptConnectionResult)>; 90 RequestHandle AcceptConnection( 91 std::vector<bt::StaticPacket< 92 pw::bluetooth::emboss::SynchronousConnectionParametersWriter>> 93 parameters, 94 AcceptConnectionCallback callback); 95 96 private: 97 using ScoRequestId = uint64_t; 98 using ConnectionResult = fit::result< 99 HostError, 100 std::pair<ScoConnection::WeakPtr, size_t /*index of parameters used*/>>; 101 using ConnectionCallback = fit::callback<void(ConnectionResult)>; 102 103 class ConnectionRequest final { 104 public: ConnectionRequest(ScoRequestId id_arg,bool initiator_arg,bool received_request_arg,std::vector<bt::StaticPacket<pw::bluetooth::emboss::SynchronousConnectionParametersWriter>> parameters_arg,ConnectionCallback callback_arg)105 ConnectionRequest( 106 ScoRequestId id_arg, 107 bool initiator_arg, 108 bool received_request_arg, 109 std::vector<bt::StaticPacket< 110 pw::bluetooth::emboss::SynchronousConnectionParametersWriter>> 111 parameters_arg, 112 ConnectionCallback callback_arg) 113 : id(id_arg), 114 initiator(initiator_arg), 115 received_request(received_request_arg), 116 parameters(std::move(parameters_arg)), 117 callback(std::move(callback_arg)) {} 118 ConnectionRequest(ConnectionRequest&&) = default; 119 ConnectionRequest& operator=(ConnectionRequest&&) = default; ~ConnectionRequest()120 ~ConnectionRequest() { 121 if (callback) { 122 bt_log(DEBUG, 123 "sco", 124 "Cancelling SCO connection request (id: %" PRIu64 ")", 125 id); 126 callback(fit::error(HostError::kCanceled)); 127 } 128 } 129 130 ScoRequestId id; 131 bool initiator; 132 bool received_request; 133 size_t current_param_index = 0; 134 std::vector<bt::StaticPacket< 135 pw::bluetooth::emboss::SynchronousConnectionParametersWriter>> 136 parameters; 137 ConnectionCallback callback; 138 }; 139 140 hci::CommandChannel::EventHandlerId AddEventHandler( 141 const hci_spec::EventCode& code, 142 hci::CommandChannel::EventCallback event_callback); 143 144 // Event handlers: 145 hci::CommandChannel::EventCallbackResult OnSynchronousConnectionComplete( 146 const hci::EventPacket& event); 147 hci::CommandChannel::EventCallbackResult OnConnectionRequest( 148 const hci::EventPacket& event); 149 150 // Returns true if parameters matching the corresponding transport were found 151 // in the current request, or false otherwise. Mutates the current request's 152 // parameter index to that of the matching parameters (or past the end on 153 // failure). 154 bool FindNextParametersThatSupportSco(); 155 bool FindNextParametersThatSupportEsco(); 156 157 ScoConnectionManager::RequestHandle QueueRequest( 158 bool initiator, 159 std::vector<bt::StaticPacket< 160 pw::bluetooth::emboss::SynchronousConnectionParametersWriter>>, 161 ConnectionCallback); 162 163 void TryCreateNextConnection(); 164 165 void CompleteRequestOrTryNextParameters(ConnectionResult); 166 167 void CompleteRequest(ConnectionResult); 168 169 void SendCommandWithStatusCallback(hci::CommandPacket command_packet, 170 hci::ResultFunction<> cb); 171 172 void SendRejectConnectionCommand(DeviceAddressBytes addr, 173 pw::bluetooth::emboss::StatusCode reason); 174 175 // If either the queued or in progress request has the given id and can be 176 // cancelled, cancel it. Called when a RequestHandle is dropped. 177 void CancelRequestWithId(ScoRequestId); 178 179 // The id that should be associated with the next request. Incremented when 180 // the current value is used. 181 ScoRequestId next_req_id_; 182 183 // If a request is made while in_progress_request_ is waiting for a complete 184 // event, it gets queued in queued_request_. 185 std::optional<ConnectionRequest> queued_request_; 186 187 std::optional<ConnectionRequest> in_progress_request_; 188 189 // Holds active connections. 190 std::unordered_map<hci_spec::ConnectionHandle, std::unique_ptr<ScoConnection>> 191 connections_; 192 193 // Handler IDs for registered events 194 std::vector<hci::CommandChannel::EventHandlerId> event_handler_ids_; 195 196 PeerId peer_id_; 197 198 const DeviceAddress local_address_; 199 const DeviceAddress peer_address_; 200 201 hci_spec::ConnectionHandle acl_handle_; 202 203 hci::Transport::WeakPtr transport_; 204 205 // Keep this as the last member to make sure that all weak pointers are 206 // invalidated before other members get destroyed. 207 WeakSelf<ScoConnectionManager> weak_ptr_factory_; 208 209 BT_DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(ScoConnectionManager); 210 }; 211 } // namespace bt::sco 212