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