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 #include "pw_bluetooth_sapphire/internal/host/gap/bredr_connection.h"
16
17 #include <utility>
18
19 namespace bt::gap {
20
21 namespace {
22
23 const char* const kInspectPeerIdPropertyName = "peer_id";
24 const char* const kInspectPairingStateNodeName = "pairing_state";
25
26 } // namespace
27
BrEdrConnection(Peer::WeakPtr peer,std::unique_ptr<hci::BrEdrConnection> link,fit::closure send_auth_request_cb,fit::callback<void ()> disconnect_cb,fit::closure on_peer_disconnect_cb,l2cap::ChannelManager * l2cap,hci::Transport::WeakPtr transport,std::optional<Request> request,pw::async::Dispatcher & dispatcher)28 BrEdrConnection::BrEdrConnection(Peer::WeakPtr peer,
29 std::unique_ptr<hci::BrEdrConnection> link,
30 fit::closure send_auth_request_cb,
31 fit::callback<void()> disconnect_cb,
32 fit::closure on_peer_disconnect_cb,
33 l2cap::ChannelManager* l2cap,
34 hci::Transport::WeakPtr transport,
35 std::optional<Request> request,
36 pw::async::Dispatcher& dispatcher)
37 : peer_id_(peer->identifier()),
38 peer_(std::move(peer)),
39 link_(std::move(link)),
40 request_(std::move(request)),
41 pairing_state_(std::make_unique<PairingState>(
42 peer_,
43 link_.get(),
44 request_ && request_->AwaitingOutgoing(),
45 std::move(send_auth_request_cb),
46 fit::bind_member<&BrEdrConnection::OnPairingStateStatus>(this))),
47 l2cap_(l2cap),
48 sco_manager_(
49 std::make_unique<sco::ScoConnectionManager>(peer_id_,
50 link_->handle(),
51 link_->peer_address(),
52 link_->local_address(),
53 transport)),
54 interrogator_(new BrEdrInterrogator(
55 peer_, link_->handle(), transport->command_channel()->AsWeakPtr())),
56 create_time_(dispatcher.now()),
57 disconnect_cb_(std::move(disconnect_cb)),
58 peer_init_token_(request_->take_peer_init_token()),
59 peer_conn_token_(peer_->MutBrEdr().RegisterConnection()),
60 dispatcher_(dispatcher) {
61 link_->set_peer_disconnect_callback(
62 [peer_disconnect_cb = std::move(on_peer_disconnect_cb)](
63 const auto& conn, auto /*reason*/) { peer_disconnect_cb(); });
64 }
65
~BrEdrConnection()66 BrEdrConnection::~BrEdrConnection() {
67 if (auto request = std::exchange(request_, std::nullopt);
68 request.has_value()) {
69 // Connection never completed so signal the requester(s).
70 request->NotifyCallbacks(ToResult(HostError::kNotSupported),
71 [] { return nullptr; });
72 }
73
74 sco_manager_.reset();
75 pairing_state_.reset();
76 link_.reset();
77 }
78
Interrogate(BrEdrInterrogator::ResultCallback callback)79 void BrEdrConnection::Interrogate(BrEdrInterrogator::ResultCallback callback) {
80 interrogator_->Start(std::move(callback));
81 }
82
OnInterrogationComplete()83 void BrEdrConnection::OnInterrogationComplete() {
84 BT_ASSERT_MSG(!interrogation_complete(),
85 "%s on a connection that's already been interrogated",
86 __FUNCTION__);
87
88 // Fulfill and clear request so that the dtor does not signal requester(s)
89 // with errors.
90 if (auto request = std::exchange(request_, std::nullopt);
91 request.has_value()) {
92 request->NotifyCallbacks(fit::ok(), [this] { return this; });
93 }
94 }
95
AddRequestCallback(BrEdrConnection::Request::OnComplete cb)96 void BrEdrConnection::AddRequestCallback(
97 BrEdrConnection::Request::OnComplete cb) {
98 if (!request_.has_value()) {
99 cb(fit::ok(), this);
100 return;
101 }
102
103 BT_ASSERT(request_);
104 request_->AddCallback(std::move(cb));
105 }
106
OpenL2capChannel(l2cap::Psm psm,l2cap::ChannelParameters params,l2cap::ChannelCallback cb)107 void BrEdrConnection::OpenL2capChannel(l2cap::Psm psm,
108 l2cap::ChannelParameters params,
109 l2cap::ChannelCallback cb) {
110 if (!interrogation_complete()) {
111 // Connection is not yet ready for L2CAP; return a null channel.
112 bt_log(INFO,
113 "gap-bredr",
114 "connection not ready; canceling connect to PSM %.4x (peer: %s)",
115 psm,
116 bt_str(peer_id_));
117 cb(l2cap::Channel::WeakPtr());
118 return;
119 }
120
121 bt_log(DEBUG,
122 "gap-bredr",
123 "opening l2cap channel on psm %#.4x (peer: %s)",
124 psm,
125 bt_str(peer_id_));
126 l2cap_->OpenL2capChannel(link().handle(), psm, params, std::move(cb));
127 }
128
OpenScoConnection(bt::StaticPacket<pw::bluetooth::emboss::SynchronousConnectionParametersWriter> parameters,sco::ScoConnectionManager::OpenConnectionCallback callback)129 BrEdrConnection::ScoRequestHandle BrEdrConnection::OpenScoConnection(
130 bt::StaticPacket<
131 pw::bluetooth::emboss::SynchronousConnectionParametersWriter>
132 parameters,
133 sco::ScoConnectionManager::OpenConnectionCallback callback) {
134 return sco_manager_->OpenConnection(std::move(parameters),
135 std::move(callback));
136 }
137
AcceptScoConnection(std::vector<bt::StaticPacket<pw::bluetooth::emboss::SynchronousConnectionParametersWriter>> parameters,sco::ScoConnectionManager::AcceptConnectionCallback callback)138 BrEdrConnection::ScoRequestHandle BrEdrConnection::AcceptScoConnection(
139 std::vector<bt::StaticPacket<
140 pw::bluetooth::emboss::SynchronousConnectionParametersWriter>>
141 parameters,
142 sco::ScoConnectionManager::AcceptConnectionCallback callback) {
143 return sco_manager_->AcceptConnection(std::move(parameters),
144 std::move(callback));
145 }
146
AttachInspect(inspect::Node & parent,std::string name)147 void BrEdrConnection::AttachInspect(inspect::Node& parent, std::string name) {
148 inspect_node_ = parent.CreateChild(name);
149 inspect_properties_.peer_id = inspect_node_.CreateString(
150 kInspectPeerIdPropertyName, peer_id_.ToString());
151
152 pairing_state_->AttachInspect(inspect_node_, kInspectPairingStateNodeName);
153 }
154
OnPairingStateStatus(hci_spec::ConnectionHandle handle,hci::Result<> status)155 void BrEdrConnection::OnPairingStateStatus(hci_spec::ConnectionHandle handle,
156 hci::Result<> status) {
157 if (bt_is_error(status,
158 DEBUG,
159 "gap-bredr",
160 "PairingState error status, disconnecting (peer id: %s)",
161 bt_str(peer_id_))) {
162 if (disconnect_cb_) {
163 disconnect_cb_();
164 }
165 return;
166 }
167
168 // Once pairing succeeds for the first time, the transition from Initializing
169 // -> Connected can happen.
170 peer_init_token_.reset();
171 }
172
duration() const173 pw::chrono::SystemClock::duration BrEdrConnection::duration() const {
174 return dispatcher_.now() - create_time_;
175 }
176
177 } // namespace bt::gap
178