• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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