xref: /aosp_15_r20/external/pigweed/pw_bluetooth_sapphire/host/hci/connection.cc (revision 61c4878ac05f98d0ceed94b57d316916de578985)
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/hci/connection.h"
16 
17 #include <cpp-string/string_printf.h>
18 #include <pw_bluetooth/hci_commands.emb.h>
19 #include <pw_bluetooth/hci_events.emb.h>
20 
21 #include <utility>
22 
23 #include "pw_bluetooth_sapphire/internal/host/common/log.h"
24 #include "pw_bluetooth_sapphire/internal/host/hci-spec/defaults.h"
25 #include "pw_bluetooth_sapphire/internal/host/hci-spec/protocol.h"
26 #include "pw_bluetooth_sapphire/internal/host/hci-spec/util.h"
27 #include "pw_bluetooth_sapphire/internal/host/transport/command_channel.h"
28 #include "pw_bluetooth_sapphire/internal/host/transport/error.h"
29 #include "pw_bluetooth_sapphire/internal/host/transport/transport.h"
30 
31 namespace bt::hci {
32 
Connection(hci_spec::ConnectionHandle handle,const DeviceAddress & local_address,const DeviceAddress & peer_address,Transport::WeakPtr hci,fit::callback<void ()> on_disconnection_complete)33 Connection::Connection(hci_spec::ConnectionHandle handle,
34                        const DeviceAddress& local_address,
35                        const DeviceAddress& peer_address,
36                        Transport::WeakPtr hci,
37                        fit::callback<void()> on_disconnection_complete)
38     : handle_(handle),
39       local_address_(local_address),
40       peer_address_(peer_address),
41       conn_state_(State::kConnected),
42       hci_(std::move(hci)),
43       weak_self_(this) {
44   PW_CHECK(hci_.is_alive());
45 
46   auto disconn_complete_handler = [self = weak_self_.GetWeakPtr(),
47                                    handle,
48                                    on_disconnection_complete_cb =
49                                        std::move(on_disconnection_complete)](
50                                       const EventPacket& event) mutable {
51     return Connection::OnDisconnectionComplete(
52         self, handle, event, std::move(on_disconnection_complete_cb));
53   };
54   hci_->command_channel()->AddEventHandler(
55       hci_spec::kDisconnectionCompleteEventCode,
56       std::move(disconn_complete_handler));
57 }
58 
~Connection()59 Connection::~Connection() {
60   if (conn_state_ == Connection::State::kConnected) {
61     Disconnect(
62         pw::bluetooth::emboss::StatusCode::REMOTE_USER_TERMINATED_CONNECTION);
63   }
64 }
65 
ToString() const66 std::string Connection::ToString() const {
67   return bt_lib_cpp_string::StringPrintf(
68       "[HCI connection (handle: %#.4x, address: %s)]",
69       handle_,
70       bt_str(peer_address_));
71 }
72 
OnDisconnectionComplete(const WeakSelf<Connection>::WeakPtr & self,hci_spec::ConnectionHandle handle,const EventPacket & event,fit::callback<void ()> on_disconnection_complete)73 CommandChannel::EventCallbackResult Connection::OnDisconnectionComplete(
74     const WeakSelf<Connection>::WeakPtr& self,
75     hci_spec::ConnectionHandle handle,
76     const EventPacket& event,
77     fit::callback<void()> on_disconnection_complete) {
78   PW_CHECK(event.event_code() == hci_spec::kDisconnectionCompleteEventCode);
79 
80   auto view =
81       event.view<pw::bluetooth::emboss::DisconnectionCompleteEventView>();
82   if (!view.Ok()) {
83     bt_log(WARN, "hci", "malformed disconnection complete event");
84     return CommandChannel::EventCallbackResult::kContinue;
85   }
86 
87   const hci_spec::ConnectionHandle event_handle =
88       view.connection_handle().Read();
89 
90   // Silently ignore this event as it isn't meant for this connection.
91   if (event_handle != handle) {
92     return CommandChannel::EventCallbackResult::kContinue;
93   }
94 
95   bt_log(INFO,
96          "hci",
97          "disconnection complete - %s, handle: %#.4x, reason: %#.2hhx (%s)",
98          bt_str(event.ToResult()),
99          handle,
100          static_cast<unsigned char>(view.reason().Read()),
101          hci_spec::StatusCodeToString(view.reason().Read()).c_str());
102 
103   if (self.is_alive()) {
104     self->conn_state_ = State::kDisconnected;
105   }
106 
107   // Peer disconnect. Callback may destroy connection.
108   if (self.is_alive() && self->peer_disconnect_callback_) {
109     self->peer_disconnect_callback_(self.get(), view.reason().Read());
110   }
111 
112   // Notify subclasses after peer_disconnect_callback_ has had a chance to clean
113   // up higher-level connections.
114   if (on_disconnection_complete) {
115     on_disconnection_complete();
116   }
117 
118   return CommandChannel::EventCallbackResult::kRemove;
119 }
120 
Disconnect(pw::bluetooth::emboss::StatusCode reason)121 void Connection::Disconnect(pw::bluetooth::emboss::StatusCode reason) {
122   PW_CHECK(conn_state_ == Connection::State::kConnected);
123 
124   conn_state_ = Connection::State::kWaitingForDisconnectionComplete;
125 
126   // Here we send a HCI_Disconnect command without waiting for it to complete.
127   auto status_cb = [](auto, const EventPacket& event) {
128     PW_DCHECK(event.event_code() == hci_spec::kCommandStatusEventCode);
129     HCI_IS_ERROR(event, TRACE, "hci", "ignoring disconnection failure");
130   };
131 
132   auto disconn =
133       CommandPacket::New<pw::bluetooth::emboss::DisconnectCommandWriter>(
134           hci_spec::kDisconnect);
135   auto params = disconn.view_t();
136   params.connection_handle().Write(handle());
137   params.reason().Write(reason);
138 
139   bt_log(DEBUG,
140          "hci",
141          "disconnecting connection (handle: %#.4x, reason: %#.2hhx)",
142          handle(),
143          static_cast<unsigned char>(reason));
144 
145   // Send HCI Disconnect.
146   hci_->command_channel()->SendCommand(std::move(disconn),
147                                        std::move(status_cb),
148                                        hci_spec::kCommandStatusEventCode);
149 }
150 
151 }  // namespace bt::hci
152