/*
* Copyright 2019 HIMSA II K/S - www.himsa.com.
* Represented by EHIMA - www.ehima.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "ble_scanner_hci_interface.h"
#include
#include
#include "hci/controller_interface.h"
#include "main/shim/entry.h"
#include "stack/include/bt_types.h"
#include "stack/include/btm_client_interface.h"
#include "stack/include/hcimsgs.h"
#include "types/raw_address.h"
using namespace bluetooth;
namespace {
BleScannerHciInterface* instance = nullptr;
static void status_callback(base::Callback cb, uint8_t* data, uint16_t len) {
uint8_t status;
log::assert_that(len == 1, "Received bad response length: {}", len);
STREAM_TO_UINT8(status, data);
cb.Run(status);
}
static void status_handle_callback(base::Callback cb, uint8_t* data,
uint16_t len) {
uint8_t status;
uint16_t handle = HCI_INVALID_HANDLE;
log::assert_that((len > 0) && (len < 4), "Received bad response length: {}", len);
uint8_t* pp = data;
STREAM_TO_UINT8(status, pp);
if (status == HCI_SUCCESS) {
log::assert_that(len == 3, "Received bad response length: {}", len);
STREAM_TO_UINT16(handle, pp);
handle = handle & 0x0EFF;
} else {
log::verbose("hci response error code: {}", int{status});
}
cb.Run(status, handle);
}
/**
* BleScannerHciInterface allows the caller to sync to a periodic advertising
* train and receive periodic advertising data events through a registered
* observer's callbacks. It also provides a synchronisation transfer API,
* including in-controller allow list support. The right feature-complete
* interface implementation is chosen during the init phase based on the
* controller's list of supported features.
*/
class BleScannerImplBase : public BleScannerHciInterface {
public:
void SetScanEventObserver(ScanEventObserver* observer) override {
// TODO: Support multiple observers if ever needed.
scan_event_observer = observer;
}
void PeriodicScanStart(uint8_t options, uint8_t set_id, uint8_t adv_addr_type,
const RawAddress& adv_addr, uint16_t skip_num, uint16_t sync_timeout,
uint8_t sync_cte_type) override {
btsnd_hcic_ble_periodic_advertising_create_sync(options, set_id, adv_addr_type, adv_addr,
skip_num, sync_timeout, sync_cte_type);
}
void PeriodicScanCancelStart(status_cb command_complete) override {
btsnd_hcic_ble_periodic_advertising_create_sync_cancel(
base::Bind(&status_callback, std::move(command_complete)));
}
void PeriodicScanTerminate(uint16_t sync_handle, status_cb command_complete) override {
btsnd_hcic_ble_periodic_advertising_terminate_sync(
sync_handle, base::Bind(&status_callback, std::move(command_complete)));
}
void PeriodicScanResultEvtEnable(uint16_t sync_handle, bool enable,
status_cb command_complete) override {
btsnd_hcic_ble_set_periodic_advertising_receive_enable(
sync_handle, enable, base::Bind(&status_callback, std::move(command_complete)));
}
void PeriodicAdvertiserListGetSize(
BleScannerHciInterface::list_size_cb command_complete) override {
command_complete.Run(bluetooth::shim::GetController()->GetLePeriodicAdvertiserListSize());
}
void PeriodicAdvertiserListAddDevice(uint8_t adv_addr_type, RawAddress& adv_addr, uint8_t set_id,
status_cb command_complete) override {
btsnd_hci_ble_add_device_to_periodic_advertiser_list(
adv_addr_type, adv_addr, set_id,
base::Bind(&status_callback, std::move(command_complete)));
}
void PeriodicAdvertiserListRemoveDevice(uint8_t adv_addr_type, RawAddress& adv_addr,
uint8_t set_id, status_cb command_complete) override {
btsnd_hci_ble_remove_device_from_periodic_advertiser_list(
adv_addr_type, adv_addr, set_id,
base::Bind(&status_callback, std::move(command_complete)));
}
void PeriodicAdvertiserListClear(status_cb command_complete) override {
btsnd_hci_ble_clear_periodic_advertiser_list(
base::Bind(&status_callback, std::move(command_complete)));
}
void PeriodicAdvSyncTransfer(const RawAddress& bd_addr, uint16_t service_data,
uint16_t sync_handle,
BleScannerHciInterface::handle_cb command_complete) override {
uint16_t acl_handle =
get_btm_client_interface().peer.BTM_GetHCIConnHandle(bd_addr, BT_TRANSPORT_LE);
if (acl_handle == HCI_INVALID_HANDLE) {
log::error("Wrong mode: no LE link exist or LE not supported");
return;
}
btsnd_hcic_ble_periodic_advertising_sync_transfer(
acl_handle, service_data, sync_handle,
base::Bind(&status_handle_callback, std::move(command_complete)));
}
void PeriodicAdvSetInfoTransfer(const RawAddress& bd_addr, uint16_t service_data,
uint8_t adv_handle, handle_cb command_complete) override {
uint16_t acl_handle =
get_btm_client_interface().peer.BTM_GetHCIConnHandle(bd_addr, BT_TRANSPORT_LE);
if (acl_handle == HCI_INVALID_HANDLE) {
log::error("Wrong mode: no LE link exist or LE not supported");
return;
}
btsnd_hcic_ble_periodic_advertising_set_info_transfer(
acl_handle, service_data, adv_handle,
base::Bind(&status_handle_callback, std::move(command_complete)));
}
void SetPeriodicAdvSyncTransferParams(const RawAddress& bd_addr, uint8_t mode, uint16_t skip,
uint16_t sync_timeout, uint8_t cte_type, bool set_defaults,
status_cb command_complete) override {
uint16_t acl_handle =
get_btm_client_interface().peer.BTM_GetHCIConnHandle(bd_addr, BT_TRANSPORT_LE);
if (acl_handle == HCI_INVALID_HANDLE) {
log::error("Wrong mode: no LE link exist or LE not supported");
return;
}
if (set_defaults) {
btsnd_hcic_ble_set_default_periodic_advertising_sync_transfer_params(
acl_handle, mode, skip, sync_timeout, cte_type,
base::Bind(&status_callback, std::move(command_complete)));
} else {
btsnd_hcic_ble_set_periodic_advertising_sync_transfer_params(
acl_handle, mode, skip, sync_timeout, cte_type,
base::Bind(&status_callback, std::move(command_complete)));
}
}
void OnPeriodicAdvSyncEstablished(uint8_t status, uint16_t sync_handle, uint8_t adv_sid,
uint8_t adv_addr_type, RawAddress adv_addr, uint8_t adv_phy,
uint16_t adv_interval, uint8_t adv_clock_accuracy) {
if (scan_event_observer) {
scan_event_observer->OnPeriodicScanEstablished(status, sync_handle, adv_sid, adv_addr_type,
adv_addr, adv_phy, adv_interval,
adv_clock_accuracy);
}
}
void OnPeriodicScanResult(uint16_t sync_handle, uint8_t tx_power, int8_t rssi, uint8_t cte_type,
uint8_t pkt_data_status, uint8_t pkt_data_len,
const uint8_t* p_pkt_data) {
// The observer should handle the caching and reassembly of the fragmented
// packet.
if (scan_event_observer) {
scan_event_observer->OnPeriodicScanResult(sync_handle, tx_power, rssi, cte_type,
pkt_data_status, pkt_data_len, p_pkt_data);
}
}
void OnPeriodicSyncLost(uint16_t sync_handle) {
if (scan_event_observer) {
scan_event_observer->OnPeriodicScanLost(sync_handle);
}
}
private:
ScanEventObserver* scan_event_observer = nullptr;
};
class BleScannerListImpl : public virtual BleScannerImplBase {
void PeriodicAdvertiserListAddDevice(uint8_t adv_addr_type, RawAddress& adv_addr, uint8_t set_id,
status_cb command_complete) override {
btsnd_hci_ble_add_device_to_periodic_advertiser_list(
adv_addr_type, adv_addr, set_id,
base::Bind(&status_callback, std::move(command_complete)));
}
void PeriodicAdvertiserListRemoveDevice(uint8_t adv_addr_type, RawAddress& adv_addr,
uint8_t set_id, status_cb command_complete) override {
btsnd_hci_ble_remove_device_from_periodic_advertiser_list(
adv_addr_type, adv_addr, set_id,
base::Bind(&status_callback, std::move(command_complete)));
}
void PeriodicAdvertiserListClear(status_cb command_complete) override {
btsnd_hci_ble_clear_periodic_advertiser_list(
base::Bind(&status_callback, std::move(command_complete)));
}
};
class BleScannerSyncTransferImpl : public virtual BleScannerImplBase {
void PeriodicAdvSyncTransfer(const RawAddress& bd_addr, uint16_t service_data,
uint16_t sync_handle,
BleScannerHciInterface::handle_cb command_complete) override {
uint16_t acl_handle =
get_btm_client_interface().peer.BTM_GetHCIConnHandle(bd_addr, BT_TRANSPORT_LE);
if (acl_handle == HCI_INVALID_HANDLE) {
log::error("Wrong mode: no LE link exist or LE not supported");
return;
}
btsnd_hcic_ble_periodic_advertising_sync_transfer(
acl_handle, service_data, sync_handle,
base::Bind(&status_handle_callback, std::move(command_complete)));
}
void PeriodicAdvSetInfoTransfer(const RawAddress& bd_addr, uint16_t service_data,
uint8_t adv_handle, handle_cb command_complete) override {
uint16_t acl_handle =
get_btm_client_interface().peer.BTM_GetHCIConnHandle(bd_addr, BT_TRANSPORT_LE);
if (acl_handle == HCI_INVALID_HANDLE) {
log::error("Wrong mode: no LE link exist or LE not supported");
return;
}
btsnd_hcic_ble_periodic_advertising_set_info_transfer(
acl_handle, service_data, adv_handle,
base::Bind(&status_handle_callback, std::move(command_complete)));
}
void SetPeriodicAdvSyncTransferParams(const RawAddress& bd_addr, uint8_t mode, uint16_t skip,
uint16_t sync_timeout, uint8_t cte_type, bool set_defaults,
status_cb command_complete) override {
uint16_t acl_handle =
get_btm_client_interface().peer.BTM_GetHCIConnHandle(bd_addr, BT_TRANSPORT_LE);
if (acl_handle == HCI_INVALID_HANDLE) {
log::error("Wrong mode: no LE link exist or LE not supported");
return;
}
if (set_defaults) {
btsnd_hcic_ble_set_default_periodic_advertising_sync_transfer_params(
acl_handle, mode, skip, sync_timeout, cte_type,
base::Bind(&status_callback, std::move(command_complete)));
} else {
btsnd_hcic_ble_set_periodic_advertising_sync_transfer_params(
acl_handle, mode, skip, sync_timeout, cte_type,
base::Bind(&status_callback, std::move(command_complete)));
}
}
};
class BleScannerCompleteImpl : public BleScannerListImpl, public BleScannerSyncTransferImpl {
// Not much to do here :)
};
} // namespace
void BleScannerHciInterface::Initialize() {
log::assert_that(instance == nullptr, "Was already initialized.");
if ((bluetooth::shim::GetController()->GetLePeriodicAdvertiserListSize()) &&
(bluetooth::shim::GetController()->SupportsBlePeriodicAdvertisingSyncTransferSender())) {
log::info("Advertiser list in controller can be used");
log::info("Periodic Adv Sync Transfer Sender role is supported");
instance = new BleScannerCompleteImpl();
} else if (bluetooth::shim::GetController()->SupportsBlePeriodicAdvertisingSyncTransferSender()) {
log::info("Periodic Adv Sync Transfer Sender role is supported");
instance = new BleScannerSyncTransferImpl();
} else if (bluetooth::shim::GetController()->GetLePeriodicAdvertiserListSize()) {
log::info("Periodic Adv Sync Transfer Recipient role is supported");
instance = new BleScannerListImpl();
}
// TODO: Implement periodic adv. sync. recipient role if ever needed.
}
BleScannerHciInterface* BleScannerHciInterface::Get() { return instance; }
void BleScannerHciInterface::CleanUp() {
delete instance;
instance = nullptr;
}