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