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/legacy_low_energy_scanner.h"
16
17 #include "pw_bluetooth_sapphire/internal/host/hci/advertising_report_parser.h"
18 #include "pw_bluetooth_sapphire/internal/host/hci/util.h"
19
20 #pragma clang diagnostic ignored "-Wswitch-enum"
21
22 namespace bt::hci {
23
LegacyLowEnergyScanner(LocalAddressDelegate * local_addr_delegate,Transport::WeakPtr transport,pw::async::Dispatcher & pw_dispatcher)24 LegacyLowEnergyScanner::LegacyLowEnergyScanner(
25 LocalAddressDelegate* local_addr_delegate,
26 Transport::WeakPtr transport,
27 pw::async::Dispatcher& pw_dispatcher)
28 : LowEnergyScanner(
29 local_addr_delegate, std::move(transport), pw_dispatcher) {
30 event_handler_id_ = hci()->command_channel()->AddLEMetaEventHandler(
31 hci_spec::kLEAdvertisingReportSubeventCode,
32 fit::bind_member<&LegacyLowEnergyScanner::OnAdvertisingReportEvent>(
33 this));
34 }
35
~LegacyLowEnergyScanner()36 LegacyLowEnergyScanner::~LegacyLowEnergyScanner() {
37 if (hci()->command_channel()) {
38 hci()->command_channel()->RemoveEventHandler(event_handler_id_);
39 }
40 }
41
StartScan(const ScanOptions & options,ScanStatusCallback callback)42 bool LegacyLowEnergyScanner::StartScan(const ScanOptions& options,
43 ScanStatusCallback callback) {
44 BT_ASSERT(options.interval >= hci_spec::kLEScanIntervalMin);
45 BT_ASSERT(options.interval <= hci_spec::kLEScanIntervalMax);
46 BT_ASSERT(options.window >= hci_spec::kLEScanIntervalMin);
47 BT_ASSERT(options.window <= hci_spec::kLEScanIntervalMax);
48 return LowEnergyScanner::StartScan(options, std::move(callback));
49 }
50
BuildSetScanParametersPacket(const DeviceAddress & local_address,const ScanOptions & options)51 EmbossCommandPacket LegacyLowEnergyScanner::BuildSetScanParametersPacket(
52 const DeviceAddress& local_address, const ScanOptions& options) {
53 auto packet = hci::EmbossCommandPacket::New<
54 pw::bluetooth::emboss::LESetScanParametersCommandWriter>(
55 hci_spec::kLESetScanParameters);
56 auto params = packet.view_t();
57
58 params.le_scan_type().Write(pw::bluetooth::emboss::LEScanType::PASSIVE);
59 if (options.active) {
60 params.le_scan_type().Write(pw::bluetooth::emboss::LEScanType::ACTIVE);
61 }
62
63 params.le_scan_interval().Write(options.interval);
64 params.le_scan_window().Write(options.window);
65 params.scanning_filter_policy().Write(options.filter_policy);
66
67 if (local_address.type() == DeviceAddress::Type::kLERandom) {
68 params.own_address_type().Write(
69 pw::bluetooth::emboss::LEOwnAddressType::RANDOM);
70 } else {
71 params.own_address_type().Write(
72 pw::bluetooth::emboss::LEOwnAddressType::PUBLIC);
73 }
74
75 return packet;
76 }
77
BuildEnablePacket(const ScanOptions & options,pw::bluetooth::emboss::GenericEnableParam enable)78 EmbossCommandPacket LegacyLowEnergyScanner::BuildEnablePacket(
79 const ScanOptions& options,
80 pw::bluetooth::emboss::GenericEnableParam enable) {
81 auto packet = EmbossCommandPacket::New<
82 pw::bluetooth::emboss::LESetScanEnableCommandWriter>(
83 hci_spec::kLESetScanEnable);
84 auto params = packet.view_t();
85 params.le_scan_enable().Write(enable);
86
87 params.filter_duplicates().Write(
88 pw::bluetooth::emboss::GenericEnableParam::DISABLE);
89 if (options.filter_duplicates) {
90 params.filter_duplicates().Write(
91 pw::bluetooth::emboss::GenericEnableParam::ENABLE);
92 }
93
94 return packet;
95 }
96
97 CommandChannel::EventCallbackResult
OnAdvertisingReportEvent(const EventPacket & event)98 LegacyLowEnergyScanner::OnAdvertisingReportEvent(const EventPacket& event) {
99 bt_log(TRACE, "hci-le", "received advertising report");
100
101 if (!IsScanning()) {
102 return CommandChannel::EventCallbackResult::kContinue;
103 }
104
105 AdvertisingReportParser parser(event);
106 const hci_spec::LEAdvertisingReportData* report;
107 int8_t rssi;
108
109 while (parser.GetNextReport(&report, &rssi)) {
110 if (report->length_data > hci_spec::kMaxLEAdvertisingDataLength) {
111 bt_log(WARN, "hci-le", "advertising data too long! Ignoring");
112 continue;
113 }
114
115 DeviceAddress address;
116 bool resolved;
117 if (!hci::DeviceAddressFromAdvReport(*report, &address, &resolved)) {
118 continue;
119 }
120
121 bool needs_scan_rsp = false;
122 bool connectable = false;
123 bool directed = false;
124
125 switch (report->event_type) {
126 case hci_spec::LEAdvertisingEventType::kAdvDirectInd:
127 directed = true;
128 break;
129 case hci_spec::LEAdvertisingEventType::kAdvInd:
130 connectable = true;
131 [[fallthrough]];
132 case hci_spec::LEAdvertisingEventType::kAdvScanInd:
133 if (IsActiveScanning()) {
134 needs_scan_rsp = true;
135 }
136 break;
137 case hci_spec::LEAdvertisingEventType::kScanRsp:
138 if (IsActiveScanning()) {
139 GetPendingResult(address)->AppendData(
140 BufferView(report->data, report->length_data));
141 LegacyLowEnergyScanner::HandleScanResponse(address, resolved, rssi);
142 }
143 continue;
144 default:
145 break;
146 }
147
148 LowEnergyScanResult result{.address = address,
149 .resolved = resolved,
150 .connectable = connectable,
151 .rssi = rssi};
152
153 if (directed) {
154 delegate()->OnDirectedAdvertisement(result);
155 continue;
156 }
157
158 if (!needs_scan_rsp) {
159 delegate()->OnPeerFound(result,
160 BufferView(report->data, report->length_data));
161 continue;
162 }
163
164 BufferView report_data = BufferView(report->data, report->length_data);
165 AddPendingResult(address, result, [this, address, resolved, rssi] {
166 LegacyLowEnergyScanner::HandleScanResponse(address, resolved, rssi);
167 });
168 GetPendingResult(address)->AppendData(report_data);
169 }
170 return CommandChannel::EventCallbackResult::kContinue;
171 }
172
173 } // namespace bt::hci
174