You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
434 lines
16 KiB
434 lines
16 KiB
/*
|
|
* 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 <base/bind.h>
|
|
|
|
#include "acl_api.h"
|
|
#include "btm_api.h"
|
|
#include "device/include/controller.h"
|
|
#include "hcidefs.h"
|
|
#include "hcimsgs.h"
|
|
|
|
#include "osi/include/log.h"
|
|
|
|
namespace {
|
|
BleScannerHciInterface* instance = nullptr;
|
|
|
|
static void status_callback(base::Callback<void(uint8_t)> cb, uint8_t* data,
|
|
uint16_t len) {
|
|
uint8_t status;
|
|
|
|
LOG_ASSERT(len == 1) << "Received bad response length: " << len;
|
|
STREAM_TO_UINT8(status, data);
|
|
|
|
DVLOG(1) << __func__ << " Received status_cb";
|
|
cb.Run(status);
|
|
}
|
|
|
|
static void status_handle_callback(base::Callback<void(uint8_t, uint16_t)> cb,
|
|
uint8_t* data, uint16_t len) {
|
|
uint8_t status;
|
|
uint16_t handle = HCI_INVALID_HANDLE;
|
|
|
|
LOG_ASSERT((len > 0) && (len < 4)) << "Received bad response length: " << len;
|
|
uint8_t* pp = data;
|
|
STREAM_TO_UINT8(status, pp);
|
|
|
|
if (status == HCI_SUCCESS) {
|
|
LOG_ASSERT(len == 3) << "Received bad response length: " << len;
|
|
|
|
STREAM_TO_UINT16(handle, pp);
|
|
handle = handle & 0x0EFF;
|
|
|
|
DVLOG(1) << __func__ << " Received status_handle_callback";
|
|
} else {
|
|
DVLOG(1) << __func__ << " 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 {
|
|
VLOG(1) << __func__;
|
|
// 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 {
|
|
VLOG(1) << __func__;
|
|
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 {
|
|
VLOG(1) << __func__;
|
|
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 {
|
|
VLOG(1) << __func__;
|
|
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 {
|
|
VLOG(1) << __func__;
|
|
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 {
|
|
VLOG(1) << __func__;
|
|
command_complete.Run(
|
|
controller_get_interface()->get_ble_periodic_advertiser_list_size());
|
|
}
|
|
|
|
void PeriodicAdvertiserListAddDevice(uint8_t adv_addr_type,
|
|
RawAddress& adv_addr, uint8_t set_id,
|
|
status_cb command_complete) override {
|
|
VLOG(1) << __func__;
|
|
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 {
|
|
VLOG(1) << __func__;
|
|
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 {
|
|
VLOG(1) << __func__;
|
|
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 {
|
|
VLOG(1) << __func__;
|
|
uint16_t acl_handle = BTM_GetHCIConnHandle(bd_addr, BT_TRANSPORT_LE);
|
|
|
|
if (acl_handle == HCI_INVALID_HANDLE) {
|
|
LOG(ERROR) << __func__
|
|
<< ": 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 {
|
|
VLOG(1) << __func__;
|
|
uint16_t acl_handle = BTM_GetHCIConnHandle(bd_addr, BT_TRANSPORT_LE);
|
|
|
|
if (acl_handle == HCI_INVALID_HANDLE) {
|
|
LOG(ERROR) << __func__
|
|
<< ": 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 {
|
|
VLOG(1) << __func__;
|
|
uint16_t acl_handle = BTM_GetHCIConnHandle(bd_addr, BT_TRANSPORT_LE);
|
|
|
|
if (acl_handle == HCI_INVALID_HANDLE) {
|
|
LOG(ERROR) << __func__
|
|
<< ": 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, 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 {
|
|
VLOG(1) << __func__;
|
|
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 {
|
|
VLOG(1) << __func__;
|
|
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 {
|
|
VLOG(1) << __func__;
|
|
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 = BTM_GetHCIConnHandle(bd_addr, BT_TRANSPORT_LE);
|
|
|
|
if (acl_handle == HCI_INVALID_HANDLE) {
|
|
LOG(ERROR) << __func__
|
|
<< ": 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 = BTM_GetHCIConnHandle(bd_addr, BT_TRANSPORT_LE);
|
|
|
|
if (acl_handle == HCI_INVALID_HANDLE) {
|
|
LOG(ERROR) << __func__
|
|
<< ": 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 = BTM_GetHCIConnHandle(bd_addr, BT_TRANSPORT_LE);
|
|
|
|
if (acl_handle == HCI_INVALID_HANDLE) {
|
|
LOG(ERROR) << __func__
|
|
<< ": 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() {
|
|
VLOG(1) << __func__;
|
|
LOG_ASSERT(instance == nullptr) << "Was already initialized.";
|
|
|
|
if ((controller_get_interface()->get_ble_periodic_advertiser_list_size()) &&
|
|
(controller_get_interface()
|
|
->supports_ble_periodic_advertising_sync_transfer_sender())) {
|
|
LOG(INFO) << "Advertiser list in controller can be used";
|
|
LOG(INFO) << "Periodic Adv Sync Transfer Sender role is supported";
|
|
instance = new BleScannerCompleteImpl();
|
|
} else if (controller_get_interface()
|
|
->supports_ble_periodic_advertising_sync_transfer_sender()) {
|
|
LOG(INFO) << "Periodic Adv Sync Transfer Sender role is supported";
|
|
instance = new BleScannerSyncTransferImpl();
|
|
} else if (controller_get_interface()
|
|
->get_ble_periodic_advertiser_list_size()) {
|
|
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() {
|
|
VLOG(1) << __func__;
|
|
|
|
delete instance;
|
|
instance = nullptr;
|
|
}
|
|
|
|
void btm_ble_process_periodic_adv_sync_est_evt(uint8_t data_len,
|
|
uint8_t* data) {
|
|
uint16_t sync_handle, adv_interval;
|
|
uint8_t status, adv_sid, adv_addr_type, adv_phy, adv_clock_accuracy;
|
|
RawAddress adv_addr;
|
|
|
|
VLOG(1) << __func__;
|
|
|
|
LOG_ASSERT(data_len == 15)
|
|
<< "Malformed LE Periodic Advertising Sync Est. Event from controller";
|
|
|
|
STREAM_TO_UINT8(status, data);
|
|
STREAM_TO_UINT16(sync_handle, data);
|
|
STREAM_TO_UINT8(adv_sid, data);
|
|
STREAM_TO_UINT8(adv_addr_type, data);
|
|
STREAM_TO_BDADDR(adv_addr, data);
|
|
STREAM_TO_UINT8(adv_phy, data);
|
|
STREAM_TO_UINT16(adv_interval, data);
|
|
STREAM_TO_UINT8(adv_clock_accuracy, data);
|
|
|
|
if (BleScannerHciInterface::Get()) {
|
|
static_cast<BleScannerImplBase*>(BleScannerHciInterface::Get())
|
|
->OnPeriodicAdvSyncEstablished(status, sync_handle, adv_sid,
|
|
adv_addr_type, adv_addr, adv_phy,
|
|
adv_interval, adv_clock_accuracy);
|
|
}
|
|
}
|
|
|
|
void btm_ble_process_periodic_adv_pkt(uint8_t data_len, uint8_t* data) {
|
|
uint8_t* p = data;
|
|
uint16_t sync_handle;
|
|
uint8_t tx_power, cte_type, pkt_data_status, pkt_data_len;
|
|
int8_t rssi;
|
|
|
|
LOG_ASSERT(data_len >= 7)
|
|
<< "Malformed LE Periodic Advertising Report Event from controller";
|
|
|
|
STREAM_TO_UINT16(sync_handle, p);
|
|
STREAM_TO_UINT8(tx_power, p);
|
|
STREAM_TO_INT8(rssi, p);
|
|
STREAM_TO_UINT8(cte_type, p);
|
|
STREAM_TO_UINT8(pkt_data_status, p);
|
|
STREAM_TO_UINT8(pkt_data_len, p);
|
|
|
|
uint8_t* pkt_data = p;
|
|
p += pkt_data_len;
|
|
|
|
if (p > data + data_len) {
|
|
LOG(ERROR) << __func__ << " Invalid pkt_data_len: " << int{pkt_data_len};
|
|
return;
|
|
}
|
|
|
|
if (rssi >= 21 && rssi <= 126) {
|
|
LOG(ERROR) << __func__
|
|
<< " bad rssi value in advertising report: " << int{rssi};
|
|
}
|
|
|
|
if (BleScannerHciInterface::Get()) {
|
|
static_cast<BleScannerImplBase*>(BleScannerHciInterface::Get())
|
|
->OnPeriodicScanResult(sync_handle, tx_power, rssi, cte_type,
|
|
pkt_data_status, pkt_data_len, pkt_data);
|
|
}
|
|
}
|
|
|
|
void btm_ble_process_periodic_adv_sync_lost_evt(uint8_t data_len,
|
|
uint8_t* data) {
|
|
uint16_t sync_handle;
|
|
|
|
if (data_len < 2) {
|
|
LOG(ERROR) << "Bogus event packet, too short";
|
|
return;
|
|
}
|
|
|
|
STREAM_TO_UINT16(sync_handle, data);
|
|
|
|
if (BleScannerHciInterface::Get()) {
|
|
static_cast<BleScannerImplBase*>(BleScannerHciInterface::Get())
|
|
->OnPeriodicSyncLost(sync_handle);
|
|
}
|
|
}
|