• 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/bredr_connection_request.h"
16 
17 #include "pw_bluetooth_sapphire/internal/host/common/log.h"
18 #include "pw_bluetooth_sapphire/internal/host/hci-spec/protocol.h"
19 
20 namespace bt::hci {
21 
CreateConnectionPacket(DeviceAddress address,std::optional<pw::bluetooth::emboss::PageScanRepetitionMode> page_scan_repetition_mode,std::optional<uint16_t> clock_offset)22 EmbossCommandPacket CreateConnectionPacket(
23     DeviceAddress address,
24     std::optional<pw::bluetooth::emboss::PageScanRepetitionMode>
25         page_scan_repetition_mode,
26     std::optional<uint16_t> clock_offset) {
27   auto request = EmbossCommandPacket::New<
28       pw::bluetooth::emboss::CreateConnectionCommandWriter>(
29       hci_spec::kCreateConnection);
30   auto params = request.view_t();
31   params.bd_addr().CopyFrom(address.value().view());
32   params.packet_type().BackingStorage().WriteUInt(kEnableAllPacketTypes);
33 
34   // The Page Scan Repetition Mode of the remote device as retrieved by Inquiry.
35   // If we do not have one for the device, opt for R2 so we will send for at
36   // least 2.56s
37   if (page_scan_repetition_mode) {
38     params.page_scan_repetition_mode().Write(*page_scan_repetition_mode);
39   } else {
40     params.page_scan_repetition_mode().Write(
41         pw::bluetooth::emboss::PageScanRepetitionMode::R2_);
42   }
43 
44   params.reserved().Write(0);  // Reserved, must be set to 0.
45 
46   // Clock Offset.  The lower 15 bits are set to the clock offset as retrieved
47   // by an Inquiry. The highest bit is set to 1 if the rest of this parameter
48   // is valid. If we don't have one, use the default.
49   if (clock_offset) {
50     params.clock_offset().valid().Write(true);
51     params.clock_offset().clock_offset().Write(*clock_offset);
52   } else {
53     params.clock_offset().valid().Write(false);
54   }
55 
56   params.allow_role_switch().Write(
57       pw::bluetooth::emboss::GenericEnableParam::DISABLE);
58 
59   return request;
60 }
61 
CreateConnection(CommandChannel * command_channel,std::optional<uint16_t> clock_offset,std::optional<pw::bluetooth::emboss::PageScanRepetitionMode> page_scan_repetition_mode,pw::chrono::SystemClock::duration timeout,OnCompleteDelegate on_command_fail)62 void BrEdrConnectionRequest::CreateConnection(
63     CommandChannel* command_channel,
64     std::optional<uint16_t> clock_offset,
65     std::optional<pw::bluetooth::emboss::PageScanRepetitionMode>
66         page_scan_repetition_mode,
67     pw::chrono::SystemClock::duration timeout,
68     OnCompleteDelegate on_command_fail) {
69   BT_DEBUG_ASSERT(timeout.count() > 0);
70 
71   // HCI Command Status Event will be sent as our completion callback.
72   auto self = weak_self_.GetWeakPtr();
73   auto complete_cb = [self,
74                       timeout,
75                       peer_id = peer_id_,
76                       on_command_fail = std::move(on_command_fail)](
77                          auto, const EventPacket& event) {
78     BT_DEBUG_ASSERT(event.event_code() == hci_spec::kCommandStatusEventCode);
79 
80     if (!self.is_alive())
81       return;
82 
83     Result<> status = event.ToResult();
84     if (status.is_error()) {
85       on_command_fail(status, peer_id);
86     } else {
87       // Both CommandChannel and the controller perform some scheduling, so log
88       // when the controller finally acknowledges Create Connection to observe
89       // outgoing connection sequencing.
90       // TODO(fxbug.dev/42173957): Added to investigate timing and can be
91       // removed if it adds no value
92       bt_log(INFO,
93              "hci-bredr",
94              "Create Connection for peer %s successfully dispatched",
95              bt_str(peer_id));
96 
97       // The request was started but has not completed; initiate the command
98       // timeout period. NOTE: The request will complete when the controller
99       // asynchronously notifies us of with a BrEdr Connection Complete event.
100       self->timeout_task_.PostAfter(timeout);
101     }
102   };
103 
104   auto packet = CreateConnectionPacket(
105       peer_address_, page_scan_repetition_mode, clock_offset);
106 
107   bt_log(INFO,
108          "hci-bredr",
109          "initiating connection request (peer: %s)",
110          bt_str(peer_id_));
111   command_channel->SendCommand(std::move(packet),
112                                std::move(complete_cb),
113                                hci_spec::kCommandStatusEventCode);
114 }
115 
116 // Status is either a Success or an Error value
CompleteRequest(Result<> status)117 Result<> BrEdrConnectionRequest::CompleteRequest(Result<> status) {
118   bt_log(INFO,
119          "hci-bredr",
120          "connection complete (status: %s, peer: %s)",
121          bt_str(status),
122          bt_str(peer_id_));
123   timeout_task_.Cancel();
124 
125   if (status.is_error()) {
126     if (state_ == RequestState::kTimedOut) {
127       return ToResult(HostError::kTimedOut);
128     }
129     if (status ==
130         ToResult(pw::bluetooth::emboss::StatusCode::UNKNOWN_CONNECTION_ID)) {
131       // The "Unknown Connection Identifier" error code is returned if this
132       // event was sent due to a successful cancellation via the
133       // HCI_Create_Connection_Cancel command
134       // See Core Spec v5.0 Vol 2, Part E, Section 7.1.7
135       state_ = RequestState::kCanceled;
136       return ToResult(HostError::kCanceled);
137     }
138   }
139   state_ = RequestState::kSuccess;
140   return status;
141 }
142 
Timeout()143 void BrEdrConnectionRequest::Timeout() {
144   // If the request was cancelled, this handler will have been removed
145   BT_ASSERT(state_ == RequestState::kPending);
146   bt_log(INFO,
147          "hci-bredr",
148          "create connection timed out: canceling request (peer: %s)",
149          bt_str(peer_id_));
150   state_ = RequestState::kTimedOut;
151   timeout_task_.Cancel();
152 }
153 
Cancel()154 bool BrEdrConnectionRequest::Cancel() {
155   if (state_ == RequestState::kSuccess) {
156     bt_log(DEBUG,
157            "hci-bredr",
158            "connection has already succeeded (peer: %s)",
159            bt_str(peer_id_));
160     return false;
161   }
162   if (state_ != RequestState::kPending) {
163     bt_log(WARN,
164            "hci-bredr",
165            "connection attempt already canceled! (peer: %s)",
166            bt_str(peer_id_));
167     return false;
168   }
169   // TODO(fxbug.dev/42143836) - We should correctly handle cancels due to a
170   // disconnect call during a pending connection creation attempt
171   bt_log(INFO,
172          "hci-bredr",
173          "canceling connection request (peer: %s)",
174          bt_str(peer_id_));
175   state_ = RequestState::kCanceled;
176   timeout_task_.Cancel();
177   return true;
178 }
179 
~BrEdrConnectionRequest()180 BrEdrConnectionRequest::~BrEdrConnectionRequest() { Cancel(); }
181 
182 }  // namespace bt::hci
183