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