• 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/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