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