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/extended_low_energy_advertiser.h"
16
17 #include <pw_assert/check.h>
18
19 #include "pw_bluetooth_sapphire/internal/host/transport/transport.h"
20
21 namespace bt::hci {
22 namespace pwemb = pw::bluetooth::emboss;
23
ExtendedLowEnergyAdvertiser(hci::Transport::WeakPtr hci_ptr,uint16_t max_advertising_data_length)24 ExtendedLowEnergyAdvertiser::ExtendedLowEnergyAdvertiser(
25 hci::Transport::WeakPtr hci_ptr, uint16_t max_advertising_data_length)
26 : LowEnergyAdvertiser(std::move(hci_ptr), max_advertising_data_length) {
27 event_handler_id_ = hci()->command_channel()->AddLEMetaEventHandler(
28 hci_spec::kLEAdvertisingSetTerminatedSubeventCode,
29 [this](const EventPacket& event) {
30 OnAdvertisingSetTerminatedEvent(event);
31 return CommandChannel::EventCallbackResult::kContinue;
32 });
33 }
34
~ExtendedLowEnergyAdvertiser()35 ExtendedLowEnergyAdvertiser::~ExtendedLowEnergyAdvertiser() {
36 // This object is probably being destroyed because the stack is shutting down,
37 // in which case the HCI layer may have already been destroyed.
38 if (!hci().is_alive() || !hci()->command_channel()) {
39 return;
40 }
41
42 hci()->command_channel()->RemoveEventHandler(event_handler_id_);
43
44 // TODO(fxbug.dev/42063496): This will only cancel one advertisement,
45 // after which the SequentialCommandRunner will have been destroyed and no
46 // further commands will be sent.
47 StopAdvertising();
48 }
49
BuildEnablePacket(const DeviceAddress & address,pwemb::GenericEnableParam enable,bool extended_pdu)50 CommandPacket ExtendedLowEnergyAdvertiser::BuildEnablePacket(
51 const DeviceAddress& address,
52 pwemb::GenericEnableParam enable,
53 bool extended_pdu) {
54 // We only enable or disable a single address at a time. The multiply by 1 is
55 // set explicitly to show that data[] within
56 // LESetExtendedAdvertisingEnableData is of size 1.
57 constexpr size_t kPacketSize =
58 pwemb::LESetExtendedAdvertisingEnableCommand::MinSizeInBytes() +
59 (1 * pwemb::LESetExtendedAdvertisingEnableData::IntrinsicSizeInBytes());
60 auto packet = hci::CommandPacket::New<
61 pwemb::LESetExtendedAdvertisingEnableCommandWriter>(
62 hci_spec::kLESetExtendedAdvertisingEnable, kPacketSize);
63 auto view = packet.view_t();
64 view.enable().Write(enable);
65 view.num_sets().Write(1);
66
67 std::optional<hci_spec::AdvertisingHandle> handle =
68 advertising_handle_map_.GetHandle(address, extended_pdu);
69 PW_CHECK(handle);
70
71 view.data()[0].advertising_handle().Write(handle.value());
72 view.data()[0].duration().Write(hci_spec::kNoAdvertisingDuration);
73 view.data()[0].max_extended_advertising_events().Write(
74 hci_spec::kNoMaxExtendedAdvertisingEvents);
75
76 return packet;
77 }
78
WriteAdvertisingEventProperties(const ExtendedLowEnergyAdvertiser::AdvertisingEventProperties & properties,pwemb::LESetExtendedAdvertisingParametersV1CommandWriter & view)79 static void WriteAdvertisingEventProperties(
80 const ExtendedLowEnergyAdvertiser::AdvertisingEventProperties& properties,
81 pwemb::LESetExtendedAdvertisingParametersV1CommandWriter& view) {
82 view.advertising_event_properties().connectable().Write(
83 properties.connectable);
84 view.advertising_event_properties().scannable().Write(properties.scannable);
85 view.advertising_event_properties().directed().Write(properties.directed);
86 view.advertising_event_properties()
87 .high_duty_cycle_directed_connectable()
88 .Write(properties.high_duty_cycle_directed_connectable);
89 view.advertising_event_properties().use_legacy_pdus().Write(
90 properties.use_legacy_pdus);
91 view.advertising_event_properties().anonymous_advertising().Write(
92 properties.anonymous_advertising);
93 view.advertising_event_properties().include_tx_power().Write(
94 properties.include_tx_power);
95 }
96
97 std::optional<CommandPacket>
BuildSetAdvertisingParams(const DeviceAddress & address,const AdvertisingEventProperties & properties,pwemb::LEOwnAddressType own_address_type,const AdvertisingIntervalRange & interval,bool extended_pdu)98 ExtendedLowEnergyAdvertiser::BuildSetAdvertisingParams(
99 const DeviceAddress& address,
100 const AdvertisingEventProperties& properties,
101 pwemb::LEOwnAddressType own_address_type,
102 const AdvertisingIntervalRange& interval,
103 bool extended_pdu) {
104 auto packet = hci::CommandPacket::New<
105 pwemb::LESetExtendedAdvertisingParametersV1CommandWriter>(
106 hci_spec::kLESetExtendedAdvertisingParameters);
107 auto view = packet.view_t();
108
109 // advertising handle
110 std::optional<hci_spec::AdvertisingHandle> handle =
111 advertising_handle_map_.MapHandle(address, extended_pdu);
112 if (!handle) {
113 bt_log(WARN,
114 "hci-le",
115 "could not allocate advertising handle for address: %s",
116 bt_str(address));
117 return std::nullopt;
118 }
119 view.advertising_handle().Write(handle.value());
120
121 WriteAdvertisingEventProperties(properties, view);
122
123 // advertising interval, NOTE: LE advertising parameters allow for up to 3
124 // octets (10 ms to 10428 s) to configure an advertising interval. However, we
125 // expose only the recommended advertising interval configurations to users,
126 // as specified in the Bluetooth Spec Volume 3, Part C, Appendix A. These
127 // values are expressed as uint16_t so we simply copy them (taking care of
128 // endianness) into the 3 octets as is.
129 view.primary_advertising_interval_min().Write(interval.min());
130 view.primary_advertising_interval_max().Write(interval.max());
131
132 // advertise on all channels
133 view.primary_advertising_channel_map().channel_37().Write(true);
134 view.primary_advertising_channel_map().channel_38().Write(true);
135 view.primary_advertising_channel_map().channel_39().Write(true);
136
137 view.own_address_type().Write(own_address_type);
138 view.advertising_filter_policy().Write(
139 pwemb::LEAdvertisingFilterPolicy::ALLOW_ALL);
140 view.advertising_tx_power().Write(
141 hci_spec::kLEExtendedAdvertisingTxPowerNoPreference);
142 view.scan_request_notification_enable().Write(
143 pwemb::GenericEnableParam::DISABLE);
144
145 // TODO(fxbug.dev/42161929): using legacy PDUs requires advertisements
146 // on the LE 1M PHY.
147 view.primary_advertising_phy().Write(pwemb::LEPrimaryAdvertisingPHY::LE_1M);
148 view.secondary_advertising_phy().Write(
149 pwemb::LESecondaryAdvertisingPHY::LE_1M);
150
151 // Payload values were initialized to zero above. By not setting the values
152 // for the following fields, we are purposely ignoring them:
153 //
154 // advertising_sid: We use only legacy PDUs, the controller ignores this field
155 // in that case peer_address: We don't support directed advertising yet
156 // peer_address_type: We don't support directed advertising yet
157 // secondary_adv_max_skip: We use only legacy PDUs, the controller ignores
158 // this field in that case
159
160 return packet;
161 }
162
163 // TODO(fxbug.dev/330935479): we can reduce code duplication by
164 // templatizing this method. However, we first have to rename
165 // advertising_data_length and advertising_data in
166 // LESetExtendedAdvertisingDataCommand to just data_length and data,
167 // respectively. We would also have to do the same in
168 // LESetExtendedScanResponseDataCommand. It's not worth it to do this rename
169 // right now since Sapphire lives in the Fuchsia repository and we would need to
170 // do an Emboss naming transition. Once Sapphire is back in the Pigweed
171 // repository, we can do this in one fell swoop rather than going through the
172 // Emboss naming transition.
BuildAdvertisingDataFragmentPacket(hci_spec::AdvertisingHandle handle,const BufferView & data,pwemb::LESetExtendedAdvDataOp operation,pwemb::LEExtendedAdvFragmentPreference fragment_preference)173 CommandPacket ExtendedLowEnergyAdvertiser::BuildAdvertisingDataFragmentPacket(
174 hci_spec::AdvertisingHandle handle,
175 const BufferView& data,
176 pwemb::LESetExtendedAdvDataOp operation,
177 pwemb::LEExtendedAdvFragmentPreference fragment_preference) {
178 size_t kPayloadSize =
179 pwemb::LESetExtendedAdvertisingDataCommandView::MinSizeInBytes().Read() +
180 data.size();
181 auto packet =
182 CommandPacket::New<pwemb::LESetExtendedAdvertisingDataCommandWriter>(
183 hci_spec::kLESetExtendedAdvertisingData, kPayloadSize);
184 auto params = packet.view_t();
185
186 params.advertising_handle().Write(handle);
187 params.operation().Write(operation);
188 params.fragment_preference().Write(fragment_preference);
189 params.advertising_data_length().Write(static_cast<uint8_t>(data.size()));
190
191 MutableBufferView data_view(params.advertising_data().BackingStorage().data(),
192 data.size());
193 data.Copy(&data_view);
194
195 return packet;
196 }
197
BuildScanResponseDataFragmentPacket(hci_spec::AdvertisingHandle handle,const BufferView & data,pwemb::LESetExtendedAdvDataOp operation,pwemb::LEExtendedAdvFragmentPreference fragment_preference)198 CommandPacket ExtendedLowEnergyAdvertiser::BuildScanResponseDataFragmentPacket(
199 hci_spec::AdvertisingHandle handle,
200 const BufferView& data,
201 pwemb::LESetExtendedAdvDataOp operation,
202 pwemb::LEExtendedAdvFragmentPreference fragment_preference) {
203 size_t kPayloadSize =
204 pwemb::LESetExtendedScanResponseDataCommandView::MinSizeInBytes().Read() +
205 data.size();
206 auto packet =
207 CommandPacket::New<pwemb::LESetExtendedScanResponseDataCommandWriter>(
208 hci_spec::kLESetExtendedScanResponseData, kPayloadSize);
209 auto params = packet.view_t();
210
211 params.advertising_handle().Write(handle);
212 params.operation().Write(operation);
213 params.fragment_preference().Write(fragment_preference);
214 params.scan_response_data_length().Write(static_cast<uint8_t>(data.size()));
215
216 MutableBufferView data_view(
217 params.scan_response_data().BackingStorage().data(), data.size());
218 data.Copy(&data_view);
219
220 return packet;
221 }
222
BuildSetAdvertisingData(const DeviceAddress & address,const AdvertisingData & data,AdvFlags flags,bool extended_pdu)223 std::vector<CommandPacket> ExtendedLowEnergyAdvertiser::BuildSetAdvertisingData(
224 const DeviceAddress& address,
225 const AdvertisingData& data,
226 AdvFlags flags,
227 bool extended_pdu) {
228 if (data.CalculateBlockSize() == 0) {
229 std::vector<CommandPacket> packets;
230 return packets;
231 }
232
233 AdvertisingData adv_data;
234 data.Copy(&adv_data);
235 if (staged_advertising_parameters_.include_tx_power_level) {
236 adv_data.SetTxPower(staged_advertising_parameters_.selected_tx_power_level);
237 }
238
239 std::optional<hci_spec::AdvertisingHandle> handle =
240 advertising_handle_map_.GetHandle(address, extended_pdu);
241 PW_CHECK(handle);
242
243 size_t block_size = adv_data.CalculateBlockSize(/*include_flags=*/true);
244 DynamicByteBuffer buffer(block_size);
245 adv_data.WriteBlock(&buffer, flags);
246
247 size_t max_length =
248 pwemb::LESetExtendedAdvertisingDataCommand::advertising_data_length_max();
249
250 // If all data fits into a single HCI packet, we don't need to do any
251 // fragmentation ourselves. The Controller may still perform fragmentation
252 // over the air but we don't have to when sending the data to the Controller.
253 if (block_size <= max_length) {
254 CommandPacket packet = BuildAdvertisingDataFragmentPacket(
255 handle.value(),
256 buffer.view(),
257 pwemb::LESetExtendedAdvDataOp::COMPLETE,
258 pwemb::LEExtendedAdvFragmentPreference::SHOULD_NOT_FRAGMENT);
259
260 std::vector<CommandPacket> packets;
261 packets.reserve(1);
262 packets.emplace_back(std::move(packet));
263 return packets;
264 }
265
266 // We have more data than will fit in a single HCI packet. Calculate the
267 // amount of packets we need to send, perform the fragmentation, and queue up
268 // the multiple LE Set Extended Advertising Data packets to the Controller.
269 size_t num_packets = block_size / max_length;
270 if (block_size % max_length != 0) {
271 num_packets++;
272 }
273
274 std::vector<CommandPacket> packets;
275 packets.reserve(num_packets);
276
277 for (size_t i = 0; i < num_packets; i++) {
278 size_t packet_size = max_length;
279 pwemb::LESetExtendedAdvDataOp operation =
280 pwemb::LESetExtendedAdvDataOp::INTERMEDIATE_FRAGMENT;
281
282 if (i == 0) {
283 operation = pwemb::LESetExtendedAdvDataOp::FIRST_FRAGMENT;
284 } else if (i == num_packets - 1) {
285 operation = pwemb::LESetExtendedAdvDataOp::LAST_FRAGMENT;
286
287 if (block_size % max_length != 0) {
288 packet_size = block_size % max_length;
289 }
290 }
291
292 size_t offset = i * max_length;
293 BufferView buffer_view(buffer.data() + offset, packet_size);
294
295 CommandPacket packet = BuildAdvertisingDataFragmentPacket(
296 handle.value(),
297 buffer_view,
298 operation,
299 pwemb::LEExtendedAdvFragmentPreference::SHOULD_NOT_FRAGMENT);
300 packets.push_back(packet);
301 }
302
303 return packets;
304 }
305
BuildUnsetAdvertisingData(const DeviceAddress & address,bool extended_pdu)306 CommandPacket ExtendedLowEnergyAdvertiser::BuildUnsetAdvertisingData(
307 const DeviceAddress& address, bool extended_pdu) {
308 constexpr size_t kPacketSize =
309 pwemb::LESetExtendedAdvertisingDataCommandView::MinSizeInBytes().Read();
310 auto packet =
311 CommandPacket::New<pwemb::LESetExtendedAdvertisingDataCommandWriter>(
312 hci_spec::kLESetExtendedAdvertisingData, kPacketSize);
313 auto payload = packet.view_t();
314
315 // advertising handle
316 std::optional<hci_spec::AdvertisingHandle> handle =
317 advertising_handle_map_.GetHandle(address, extended_pdu);
318 PW_CHECK(handle);
319 payload.advertising_handle().Write(handle.value());
320
321 payload.operation().Write(pwemb::LESetExtendedAdvDataOp::COMPLETE);
322 payload.fragment_preference().Write(
323 pwemb::LEExtendedAdvFragmentPreference::SHOULD_NOT_FRAGMENT);
324 payload.advertising_data_length().Write(0);
325
326 return packet;
327 }
328
BuildSetScanResponse(const DeviceAddress & address,const AdvertisingData & data,bool extended_pdu)329 std::vector<CommandPacket> ExtendedLowEnergyAdvertiser::BuildSetScanResponse(
330 const DeviceAddress& address,
331 const AdvertisingData& data,
332 bool extended_pdu) {
333 if (data.CalculateBlockSize() == 0) {
334 std::vector<CommandPacket> packets;
335 return packets;
336 }
337
338 AdvertisingData scan_rsp;
339 data.Copy(&scan_rsp);
340 if (staged_advertising_parameters_.include_tx_power_level) {
341 scan_rsp.SetTxPower(staged_advertising_parameters_.selected_tx_power_level);
342 }
343
344 std::optional<hci_spec::AdvertisingHandle> handle =
345 advertising_handle_map_.GetHandle(address, extended_pdu);
346 PW_CHECK(handle);
347
348 size_t block_size = scan_rsp.CalculateBlockSize(/*include_flags=*/false);
349 DynamicByteBuffer buffer(block_size);
350 scan_rsp.WriteBlock(&buffer, std::nullopt);
351
352 size_t max_length = pwemb::LESetExtendedScanResponseDataCommand::
353 scan_response_data_length_max();
354
355 // If all data fits into a single HCI packet, we don't need to do any
356 // fragmentation ourselves. The Controller may still perform fragmentation
357 // over the air but we don't have to when sending the data to the Controller.
358 if (block_size <= max_length) {
359 CommandPacket packet = BuildScanResponseDataFragmentPacket(
360 handle.value(),
361 buffer.view(),
362 pwemb::LESetExtendedAdvDataOp::COMPLETE,
363 pwemb::LEExtendedAdvFragmentPreference::SHOULD_NOT_FRAGMENT);
364
365 std::vector<CommandPacket> packets;
366 packets.reserve(1);
367 packets.emplace_back(std::move(packet));
368 return packets;
369 }
370
371 // We have more data than will fit in a single HCI packet. Calculate the
372 // amount of packets we need to send, perform the fragmentation, and queue up
373 // the multiple LE Set Extended Advertising Data packets to the Controller.
374 size_t num_packets = block_size / max_length;
375 if (block_size % max_length != 0) {
376 num_packets++;
377 }
378
379 std::vector<CommandPacket> packets;
380 packets.reserve(num_packets);
381
382 for (size_t i = 0; i < num_packets; i++) {
383 size_t packet_size = max_length;
384 pwemb::LESetExtendedAdvDataOp operation =
385 pwemb::LESetExtendedAdvDataOp::INTERMEDIATE_FRAGMENT;
386
387 if (i == 0) {
388 operation = pwemb::LESetExtendedAdvDataOp::FIRST_FRAGMENT;
389 } else if (i == num_packets - 1) {
390 operation = pwemb::LESetExtendedAdvDataOp::LAST_FRAGMENT;
391
392 if (block_size % max_length != 0) {
393 packet_size = block_size % max_length;
394 }
395 }
396
397 size_t offset = i * max_length;
398 BufferView buffer_view(buffer.data() + offset, packet_size);
399
400 CommandPacket packet = BuildScanResponseDataFragmentPacket(
401 handle.value(),
402 buffer_view,
403 operation,
404 pwemb::LEExtendedAdvFragmentPreference::SHOULD_NOT_FRAGMENT);
405 packets.push_back(packet);
406 }
407
408 return packets;
409 }
410
BuildUnsetScanResponse(const DeviceAddress & address,bool extended_pdu)411 CommandPacket ExtendedLowEnergyAdvertiser::BuildUnsetScanResponse(
412 const DeviceAddress& address, bool extended_pdu) {
413 constexpr size_t kPacketSize =
414 pwemb::LESetExtendedScanResponseDataCommandView::MinSizeInBytes().Read();
415 auto packet =
416 CommandPacket::New<pwemb::LESetExtendedScanResponseDataCommandWriter>(
417 hci_spec::kLESetExtendedScanResponseData, kPacketSize);
418 auto payload = packet.view_t();
419
420 // advertising handle
421 std::optional<hci_spec::AdvertisingHandle> handle =
422 advertising_handle_map_.GetHandle(address, extended_pdu);
423 PW_CHECK(handle);
424 payload.advertising_handle().Write(handle.value());
425
426 payload.operation().Write(pwemb::LESetExtendedAdvDataOp::COMPLETE);
427 payload.fragment_preference().Write(
428 pwemb::LEExtendedAdvFragmentPreference::SHOULD_NOT_FRAGMENT);
429 payload.scan_response_data_length().Write(0);
430
431 return packet;
432 }
433
BuildRemoveAdvertisingSet(const DeviceAddress & address,bool extended_pdu)434 CommandPacket ExtendedLowEnergyAdvertiser::BuildRemoveAdvertisingSet(
435 const DeviceAddress& address, bool extended_pdu) {
436 std::optional<hci_spec::AdvertisingHandle> handle =
437 advertising_handle_map_.GetHandle(address, extended_pdu);
438 PW_CHECK(handle);
439 auto packet =
440 hci::CommandPacket::New<pwemb::LERemoveAdvertisingSetCommandWriter>(
441 hci_spec::kLERemoveAdvertisingSet);
442 auto view = packet.view_t();
443 view.advertising_handle().Write(handle.value());
444
445 return packet;
446 }
447
OnSetAdvertisingParamsComplete(const EventPacket & event)448 void ExtendedLowEnergyAdvertiser::OnSetAdvertisingParamsComplete(
449 const EventPacket& event) {
450 auto event_view = event.view<pw::bluetooth::emboss::EventHeaderView>();
451 PW_CHECK(event_view.event_code().Read() ==
452 pw::bluetooth::emboss::EventCode::COMMAND_COMPLETE);
453
454 auto cmd_complete_view =
455 event.view<pw::bluetooth::emboss::CommandCompleteEventView>();
456 PW_CHECK(
457 cmd_complete_view.command_opcode().Read() ==
458 pw::bluetooth::emboss::OpCode::LE_SET_EXTENDED_ADVERTISING_PARAMETERS_V1);
459
460 Result<> result = event.ToResult();
461 if (bt_is_error(result,
462 WARN,
463 "hci-le",
464 "set advertising parameters, error received: %s",
465 bt_str(result))) {
466 return; // full error handling done in super class, can just return here
467 }
468
469 auto view = event.view<
470 pw::bluetooth::emboss::
471 LESetExtendedAdvertisingParametersCommandCompleteEventView>();
472 if (staged_advertising_parameters_.include_tx_power_level) {
473 staged_advertising_parameters_.selected_tx_power_level =
474 view.selected_tx_power().Read();
475 }
476 }
477
StartAdvertising(const DeviceAddress & address,const AdvertisingData & data,const AdvertisingData & scan_rsp,const AdvertisingOptions & options,ConnectionCallback connect_callback,ResultFunction<> result_callback)478 void ExtendedLowEnergyAdvertiser::StartAdvertising(
479 const DeviceAddress& address,
480 const AdvertisingData& data,
481 const AdvertisingData& scan_rsp,
482 const AdvertisingOptions& options,
483 ConnectionCallback connect_callback,
484 ResultFunction<> result_callback) {
485 // if there is an operation currently in progress, enqueue this operation and
486 // we will get to it the next time we have a chance
487 if (!hci_cmd_runner().IsReady()) {
488 bt_log(INFO,
489 "hci-le",
490 "hci cmd runner not ready, queuing advertisement commands for now");
491
492 AdvertisingData copied_data;
493 data.Copy(&copied_data);
494
495 AdvertisingData copied_scan_rsp;
496 scan_rsp.Copy(&copied_scan_rsp);
497
498 op_queue_.push([this,
499 address_copy = address,
500 data_copy = std::move(copied_data),
501 scan_rsp_copy = std::move(copied_scan_rsp),
502 options_copy = options,
503 conn_cb = std::move(connect_callback),
504 result_cb = std::move(result_callback)]() mutable {
505 StartAdvertising(address_copy,
506 data_copy,
507 scan_rsp_copy,
508 options_copy,
509 std::move(conn_cb),
510 std::move(result_cb));
511 });
512
513 return;
514 }
515
516 fit::result<HostError> result =
517 CanStartAdvertising(address, data, scan_rsp, options, connect_callback);
518 if (result.is_error()) {
519 result_callback(ToResult(result.error_value()));
520 return;
521 }
522
523 if (IsAdvertising(address, options.extended_pdu)) {
524 bt_log(DEBUG,
525 "hci-le",
526 "updating existing advertisement for %s",
527 bt_str(address));
528 }
529
530 staged_advertising_parameters_.clear();
531 staged_advertising_parameters_.include_tx_power_level =
532 options.include_tx_power_level;
533 staged_advertising_parameters_.extended_pdu = options.extended_pdu;
534
535 // Core Spec, Volume 4, Part E, Section 7.8.58: "the number of advertising
536 // sets that can be supported is not fixed and the Controller can change it at
537 // any time. The memory used to store advertising sets can also be used for
538 // other purposes."
539 //
540 // Depending on the memory profile of the controller, a new advertising set
541 // may or may not be accepted. We could use
542 // HCI_LE_Read_Number_of_Supported_Advertising_Sets to check if the controller
543 // has space for another advertising set. However, the value may change after
544 // the read and before the addition of the advertising set. Furthermore,
545 // sending an extra HCI command increases the latency of our stack. Instead,
546 // we simply attempt to add. If the controller is unable to support another
547 // advertising set, it will respond with a memory capacity exceeded error.
548 StartAdvertisingInternal(address,
549 data,
550 scan_rsp,
551 options,
552 std::move(connect_callback),
553 std::move(result_callback));
554 }
555
StopAdvertising()556 void ExtendedLowEnergyAdvertiser::StopAdvertising() {
557 LowEnergyAdvertiser::StopAdvertising();
558 advertising_handle_map_.Clear();
559
560 // std::queue doesn't have a clear method so we have to resort to this
561 // tomfoolery :(
562 decltype(op_queue_) empty;
563 std::swap(op_queue_, empty);
564 }
565
StopAdvertising(const DeviceAddress & address,bool extended_pdu)566 void ExtendedLowEnergyAdvertiser::StopAdvertising(const DeviceAddress& address,
567 bool extended_pdu) {
568 // if there is an operation currently in progress, enqueue this operation and
569 // we will get to it the next time we have a chance
570 if (!hci_cmd_runner().IsReady()) {
571 bt_log(
572 INFO,
573 "hci-le",
574 "hci cmd runner not ready, queueing stop advertising command for now");
575 op_queue_.push([this, address, extended_pdu]() {
576 StopAdvertising(address, extended_pdu);
577 });
578 return;
579 }
580
581 LowEnergyAdvertiser::StopAdvertisingInternal(address, extended_pdu);
582 advertising_handle_map_.RemoveAddress(address, extended_pdu);
583 }
584
OnIncomingConnection(hci_spec::ConnectionHandle handle,pwemb::ConnectionRole role,const DeviceAddress & peer_address,const hci_spec::LEConnectionParameters & conn_params)585 void ExtendedLowEnergyAdvertiser::OnIncomingConnection(
586 hci_spec::ConnectionHandle handle,
587 pwemb::ConnectionRole role,
588 const DeviceAddress& peer_address,
589 const hci_spec::LEConnectionParameters& conn_params) {
590 // Core Spec Volume 4, Part E, Section 7.8.56: Incoming connections to LE
591 // Extended Advertising occur through two events: HCI_LE_Connection_Complete
592 // and HCI_LE_Advertising_Set_Terminated. This method is called as a result of
593 // the HCI_LE_Connection_Complete event. At this point, we only have a
594 // connection handle but don't know the locally advertised address that the
595 // connection is for. Until we receive the HCI_LE_Advertising_Set_Terminated
596 // event, we stage these parameters.
597 staged_connections_[handle] = {role, peer_address, conn_params};
598 }
599
600 // The HCI_LE_Advertising_Set_Terminated event contains the mapping between
601 // connection handle and advertising handle. After the
602 // HCI_LE_Advertising_Set_Terminated event, we have all the information
603 // necessary to create a connection object within the Host layer.
OnAdvertisingSetTerminatedEvent(const EventPacket & event)604 void ExtendedLowEnergyAdvertiser::OnAdvertisingSetTerminatedEvent(
605 const EventPacket& event) {
606 Result<> result = event.ToResult();
607 if (bt_is_error(result,
608 ERROR,
609 "hci-le",
610 "advertising set terminated event, error received %s",
611 bt_str(result))) {
612 return;
613 }
614
615 auto params = event.view<pwemb::LEAdvertisingSetTerminatedSubeventView>();
616
617 hci_spec::ConnectionHandle connection_handle =
618 params.connection_handle().Read();
619 auto staged_parameters_node = staged_connections_.extract(connection_handle);
620
621 if (staged_parameters_node.empty()) {
622 bt_log(ERROR,
623 "hci-le",
624 "advertising set terminated event, staged params not available "
625 "(handle: %d)",
626 params.advertising_handle().Read());
627 return;
628 }
629
630 hci_spec::AdvertisingHandle adv_handle = params.advertising_handle().Read();
631 std::optional<DeviceAddress> opt_local_address =
632 advertising_handle_map_.GetAddress(adv_handle);
633
634 // We use the identity address as the local address if we aren't advertising
635 // or otherwise don't know about this advertising set. This is obviously
636 // wrong. However, the link will be disconnected in that case before it can
637 // propagate to higher layers.
638 static DeviceAddress identity_address =
639 DeviceAddress(DeviceAddress::Type::kLEPublic, {0});
640 DeviceAddress local_address = identity_address;
641 if (opt_local_address) {
642 local_address = opt_local_address.value();
643 }
644
645 StagedConnectionParameters staged = staged_parameters_node.mapped();
646
647 CompleteIncomingConnection(connection_handle,
648 staged.role,
649 local_address,
650 staged.peer_address,
651 staged.conn_params,
652 staged_advertising_parameters_.extended_pdu);
653
654 staged_advertising_parameters_.clear();
655 }
656
OnCurrentOperationComplete()657 void ExtendedLowEnergyAdvertiser::OnCurrentOperationComplete() {
658 if (op_queue_.empty()) {
659 return; // no more queued operations so nothing to do
660 }
661
662 fit::closure closure = std::move(op_queue_.front());
663 op_queue_.pop();
664 closure();
665 }
666
667 } // namespace bt::hci
668