* Copyright (C) 2021-2022 Huawei Device Co., Ltd.
* 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 "btm_acl.h"
#include <securec.h>
#include "hci/hci.h"
#include "hci/hci_le_controller_5_0.h"
#include "hci/hci_error.h"
#include "log.h"
#include "platform/include/alarm.h"
#include "platform/include/allocator.h"
#include "platform/include/list.h"
#include "platform/include/mutex.h"
#include "platform/include/queue.h"
#include "platform/include/semaphore.h"
#include "platform/include/thread.h"
#include "btm.h"
#include "btm_acl_def.h"
#include "btm_controller.h"
#include "btm_inq_db.h"
#include "btm_le_sec.h"
#include "btm_thread.h"
#include "btm_wl.h"
#define STATUS_NONE 0
#define STATUS_INITIALIZED 1
#define IS_INITIALIZED() (g_status == STATUS_INITIALIZED)
#define CLEANUP_TIMEOUT 5000
#define ACTION_ADD_TO_WHITE_LIST 1
#define ACTION_REMOVE_FORM_WHITE_LIST 2
typedef struct {
uint8_t action;
BtAddr addr;
} BtmWhiteListPendingAction;
typedef struct {
const BtmAclCallbacks *callbacks;
void *context;
} BtmAclCallbacksBlock;
static List *g_aclList = NULL;
Mutex *g_aclListLock = NULL;
static List *g_aclCallbackList = NULL;
static Mutex *g_aclCallbackListLock = NULL;
List *g_remoteSupportRequestList = NULL;
static HciEventCallbacks g_hciEventCallbacks;
static Mutex *g_cleanupMutex = NULL;
static uint16_t g_cleanupHandle = 0xffff;
static Semaphore *g_cleanupEvent = NULL;
static Alarm *g_cleanupTimer = NULL;
static uint8_t g_status = STATUS_NONE;
static uint16_t g_leScanInterval = LE_SCAN_INTERVAL_DEFAULT;
static uint16_t g_leScanWindow = LE_SCAN_WINDOW_DEFAULT;
static Mutex *g_leConnectionCancelLock = NULL;
static List *g_leConnectionCancelList = NULL;
static Queue *g_leWhiteListPendingActionQueue;
static Mutex *g_autoConnectLock = NULL;
static BtmAclConnection *AllocAclConnection()
{
BtmAclConnection *connection = MEM_CALLOC.alloc(sizeof(BtmAclConnection));
if (connection != NULL) {
connection->refCount = 0;
connection->isInitiator = false;
connection->timeoutTimer = AlarmCreate(NULL, false);
connection->remoteFeatures.bredr.featureStatus = REQUEST_NOT_COMPLETED;
connection->remoteFeatures.bredr.extendedFeatureStatus = REQUEST_NOT_COMPLETED;
connection->remoteFeatures.le.featureStatus = REQUEST_NOT_COMPLETED;
}
return connection;
}
static void FreeAclConnection(void *connection)
{
BtmAclConnection *aclConnection = (BtmAclConnection *)connection;
if (aclConnection != NULL) {
if (aclConnection->timeoutTimer != NULL) {
AlarmCancel(aclConnection->timeoutTimer);
AlarmDelete(aclConnection->timeoutTimer);
aclConnection->timeoutTimer = NULL;
}
}
MEM_CALLOC.free(connection);
}
static BtmAclCallbacksBlock *BtmAllocAclCallbacksBlock(const BtmAclCallbacks *callbacks, void *context)
{
BtmAclCallbacksBlock *block = MEM_CALLOC.alloc(sizeof(BtmAclCallbacksBlock));
if (block != NULL) {
block->callbacks = callbacks;
block->context = context;
}
return block;
}
static void BtmFreeAclCallbacksBlock(void *block)
{
MEM_CALLOC.free(block);
}
static void BtmAclAllocRes()
{
g_aclListLock = MutexCreate();
g_aclList = ListCreate(FreeAclConnection);
g_remoteSupportRequestList = ListCreate(MEM_MALLOC.free);
g_aclCallbackListLock = MutexCreate();
g_aclCallbackList = ListCreate(BtmFreeAclCallbacksBlock);
g_cleanupMutex = MutexCreate();
g_cleanupEvent = SemaphoreCreate(0);
g_cleanupTimer = AlarmCreate("AclCleanup", false);
g_leScanInterval = LE_SCAN_INTERVAL_DEFAULT;
g_leScanWindow = LE_SCAN_WINDOW_DEFAULT;
g_leConnectionCancelLock = MutexCreate();
g_leConnectionCancelList = ListCreate(MEM_MALLOC.free);
g_leWhiteListPendingActionQueue = QueueCreate(UINT16_MAX);
g_autoConnectLock = MutexCreate();
}
void BtmInitAcl()
{
LOG_DEBUG("%{public}s start", __FUNCTION__);
BtmAclAllocRes();
g_status = STATUS_INITIALIZED;
LOG_DEBUG("%{public}s end", __FUNCTION__);
}
static void BtmAclFreeRes()
{
if (g_cleanupMutex != NULL) {
MutexDelete(g_cleanupMutex);
g_cleanupMutex = NULL;
}
if (g_cleanupEvent != NULL) {
SemaphoreDelete(g_cleanupEvent);
g_cleanupEvent = NULL;
}
if (g_cleanupTimer != NULL) {
AlarmDelete(g_cleanupTimer);
g_cleanupTimer = NULL;
}
if (g_aclList != NULL) {
ListDelete(g_aclList);
g_aclList = NULL;
}
if (g_remoteSupportRequestList != NULL) {
ListDelete(g_remoteSupportRequestList);
g_remoteSupportRequestList = NULL;
}
if (g_aclListLock != NULL) {
MutexDelete(g_aclListLock);
g_aclListLock = NULL;
}
if (g_aclCallbackListLock != NULL) {
MutexDelete(g_aclCallbackListLock);
g_aclCallbackListLock = NULL;
}
if (g_aclCallbackList != NULL) {
ListDelete(g_aclCallbackList);
g_aclCallbackList = NULL;
}
if (g_leConnectionCancelList != NULL) {
ListDelete(g_leConnectionCancelList);
g_leConnectionCancelList = NULL;
}
if (g_leConnectionCancelLock != NULL) {
MutexDelete(g_leConnectionCancelLock);
g_leConnectionCancelLock = NULL;
}
if (g_autoConnectLock != NULL) {
MutexDelete(g_autoConnectLock);
g_autoConnectLock = NULL;
}
if (g_leWhiteListPendingActionQueue != NULL) {
QueueDelete(g_leWhiteListPendingActionQueue, MEM_MALLOC.free);
g_leWhiteListPendingActionQueue = NULL;
}
}
void BtmCloseAcl()
{
LOG_DEBUG("%{public}s start", __FUNCTION__);
g_status = STATUS_NONE;
BtmAclFreeRes();
LOG_DEBUG("%{public}s end", __FUNCTION__);
}
void BtmStartAcl()
{
g_leScanInterval = LE_SCAN_INTERVAL_DEFAULT;
g_leScanWindow = LE_SCAN_WINDOW_DEFAULT;
HCI_RegisterEventCallbacks(&g_hciEventCallbacks);
}
void BtmStopAcl()
{
HCI_DeregisterEventCallbacks(&g_hciEventCallbacks);
MutexLock(g_aclListLock);
ListClear(g_aclList);
ListClear(g_remoteSupportRequestList);
MutexUnlock(g_aclListLock);
MutexLock(g_aclCallbackListLock);
ListClear(g_aclCallbackList);
MutexUnlock(g_aclCallbackListLock);
MutexLock(g_leConnectionCancelLock);
ListClear(g_leConnectionCancelList);
MutexUnlock(g_leConnectionCancelLock);
}
static bool IsEqualAddr(const uint8_t addr1[BT_ADDRESS_SIZE], const uint8_t addr2[BT_ADDRESS_SIZE])
{
bool isEqual = true;
for (uint8_t i = 0; i < BT_ADDRESS_SIZE; i++) {
if (addr1[i] != addr2[i]) {
isEqual = false;
break;
}
}
return isEqual;
}
static BtmAclConnection *BtmAclFindConnectionByAddr(const BtAddr *addr)
{
BtmAclConnection *connection = NULL;
ListNode *node = ListGetFirstNode(g_aclList);
while (node != NULL) {
connection = ListGetNodeData(node);
if (connection->transport == TRANSPORT_BREDR_STACK) {
if (IsEqualAddr(connection->addr.addr, addr->addr)) {
break;
}
}
connection = NULL;
node = ListGetNextNode(node);
}
return connection;
}
static BtmAclConnection *BtmAclFindLeConnectionByAddr(const BtAddr *addr)
{
BtmAclConnection *connection = NULL;
ListNode *node = ListGetFirstNode(g_aclList);
while (node != NULL) {
connection = ListGetNodeData(node);
if (connection->transport == TRANSPORT_LE_STACK) {
if (IsEqualAddr(connection->addr.addr, addr->addr)) {
break;
}
}
connection = NULL;
node = ListGetNextNode(node);
}
return connection;
}
BtmAclConnection *BtmAclFindConnectionByHandle(uint16_t handle)
{
BtmAclConnection *connection = NULL;
ListNode *node = ListGetFirstNode(g_aclList);
while (node != NULL) {
connection = ListGetNodeData(node);
if (connection != NULL) {
if (connection->connectionHandle == handle) {
break;
}
connection = NULL;
}
node = ListGetNextNode(node);
}
return connection;
}
static int BtmAclCreateConnection(const BtAddr *addr)
{
uint8_t pageScanRepetitionMode = HCI_PAGE_SCAN_REPETITION_MODE_R1;
uint16_t clockOffset = 0;
BtmInquiryInfo inquiryInfo = {0};
int queryResult = BtmQueryInquiryInfoByAddr(addr, &inquiryInfo);
if (queryResult == BT_SUCCESS) {
pageScanRepetitionMode = inquiryInfo.pageScanRepetitionMode;
clockOffset = inquiryInfo.clockOffset;
}
HciCreateConnectionParam param = {
.bdAddr =
{
.raw = {0},
},
.packetType = (HCI_PACKET_TYPE_DM1 | HCI_PACKET_TYPE_DH1 | HCI_PACKET_TYPE_DM3 | HCI_PACKET_TYPE_DH3 |
HCI_PACKET_TYPE_DM5 | HCI_PACKET_TYPE_DH5),
.pageScanRepetitionMode = pageScanRepetitionMode,
.reserved = 0,
.clockOffset = clockOffset,
.allowRoleSwitch = BTM_IsControllerSupportRoleSwitch() ? HCI_ALLOW_ROLE_SWITCH : HCI_NOT_ALLOW_ROLE_SWITCH,
};
(void)memcpy_s(param.bdAddr.raw, BT_ADDRESS_SIZE, addr->addr, BT_ADDRESS_SIZE);
return HCI_CreateConnection(¶m);
}
int BTM_AclConnect(const BtAddr *addr)
{
if (addr == NULL) {
return BT_BAD_PARAM;
}
if (!IS_INITIALIZED()) {
return BT_BAD_STATUS;
}
int result = BT_SUCCESS;
MutexLock(g_aclListLock);
if (ListGetSize(g_aclList) >= BTM_MAX_ACL) {
MutexUnlock(g_aclListLock);
result = BT_CONNECT_NUM_MAX;
} else {
BtmAclConnection *connection = BtmAclFindConnectionByAddr(addr);
if (connection != NULL) {
if (connection->state != CONNECTING) {
result = BT_BAD_STATUS;
}
MutexUnlock(g_aclListLock);
} else {
connection = AllocAclConnection();
if (connection != NULL) {
connection->addr = *addr;
connection->transport = TRANSPORT_BREDR_STACK;
connection->isInitiator = true;
connection->state = CONNECTING;
ListAddLast(g_aclList, connection);
} else {
MutexUnlock(g_aclListLock);
return BT_NO_MEMORY;
}
result = BtmAclCreateConnection(addr);
if (result != BT_SUCCESS) {
ListRemoveNode(g_aclList, connection);
}
MutexUnlock(g_aclListLock);
}
}
return result;
}
static void BtmAclTimeoutTask(void *context)
{
BtmAclConnection *connection = (BtmAclConnection *)context;
MutexLock(g_aclListLock);
ListNode *node = ListGetFirstNode(g_aclList);
while (node != NULL) {
if (connection == ListGetNodeData(node)) {
if (connection->refCount == 0) {
connection->state = DISCONNECTING;
HciDisconnectParam disconnectParam = {
.connectionHandle = connection->connectionHandle,
.reason = HCI_REMOTE_USER_TERMINATED_CONNECTION,
};
HCI_Disconnect(&disconnectParam);
}
break;
}
node = ListGetNextNode(node);
}
MutexUnlock(g_aclListLock);
}
static void BtmAclTimeout(void *context)
{
Thread *thread = BTM_GetProcessingThread();
if (thread != NULL) {
ThreadPostTask(thread, BtmAclTimeoutTask, context);
}
}
NO_SANITIZE("cfi") static void BtmOnConnectionComplete(const HciConnectionCompleteEventParam *eventParam)
{
if (eventParam->linkType != HCI_LINK_TYPE_ACL ||
(eventParam->status == HCI_CONNECTION_ALREADY_EXISTS)) {
return;
}
BtAddr addr = {0};
addr.type = BT_PUBLIC_DEVICE_ADDRESS;
(void)memcpy_s(addr.addr, BT_ADDRESS_SIZE, eventParam->bdAddr.raw, BT_ADDRESS_SIZE);
uint8_t cod[COD_SIZE] = {0, 0, 0};
MutexLock(g_aclListLock);
BtmAclConnection *connection = BtmAclFindConnectionByAddr(&addr);
if (connection != NULL) {
if (eventParam->status == HCI_SUCCESS) {
connection->connectionHandle = eventParam->connectionHandle;
connection->state = CONNECTED;
(void)memcpy_s(cod, COD_SIZE, connection->remoteCod, COD_SIZE);
AlarmSet(connection->timeoutTimer,
connection->isInitiator ? ACL_TIMEOUT : ACL_PASSIVE_TIMEOUT,
BtmAclTimeout,
connection);
} else {
ListRemoveNode(g_aclList, connection);
}
}
MutexUnlock(g_aclListLock);
if (eventParam->status == HCI_SUCCESS) {
HciReadRemoteVersionInformationParam cmdParam = {0};
cmdParam.connectionHandle = eventParam->connectionHandle;
HCI_ReadRemoteVersionInformation(&cmdParam);
}
BtmAclConnectCompleteParam connectCompleteParam = {0};
connectCompleteParam.status = eventParam->status;
connectCompleteParam.connectionHandle = eventParam->connectionHandle;
connectCompleteParam.addr = &addr;
(void)memcpy_s(connectCompleteParam.classOfDevice, COD_SIZE, cod, COD_SIZE);
connectCompleteParam.encyptionEnabled = eventParam->encryptionEnabled;
MutexLock(g_aclCallbackListLock);
ListNode *node = ListGetFirstNode(g_aclCallbackList);
BtmAclCallbacksBlock *block = NULL;
while (node != NULL) {
block = (BtmAclCallbacksBlock *)ListGetNodeData(node);
if (block->callbacks->connectionComplete != NULL) {
block->callbacks->connectionComplete(&connectCompleteParam, block->context);
}
node = ListGetNextNode(node);
}
MutexUnlock(g_aclCallbackListLock);
}
static void BtmOnConnectionIndication(const HciConnectionIndicationEventParam *eventParam)
{
if (eventParam->linkType != HCI_LINK_TYPE_ACL) {
return;
}
BtAddr addr = {
.addr = {0},
.type = BT_PUBLIC_DEVICE_ADDRESS,
};
(void)memcpy_s(addr.addr, BT_ADDRESS_SIZE, eventParam->bdAddr.raw, BT_ADDRESS_SIZE);
MutexLock(g_aclListLock);
if (ListGetSize(g_aclList) >= BTM_MAX_ACL) {
MutexUnlock(g_aclListLock);
HciRejectConnectionRequestParam param = {
.bdAddr = eventParam->bdAddr,
.reason = HCI_CONNECTION_REJECTED_DUE_TO_LIMITED_RESOURCES,
};
HCI_RejectConnectionRequest(¶m);
return;
}
BtmAclConnection *connection = AllocAclConnection();
if (connection == NULL) {
MutexUnlock(g_aclListLock);
return;
}
connection->addr = addr;
connection->transport = TRANSPORT_BREDR_STACK;
connection->state = CONNECTING;
connection->isInitiator = false;
(void)memcpy_s(connection->remoteCod, COD_SIZE, eventParam->classOfDevice, COD_SIZE);
uint8_t role = HCI_ROLE_SLAVE;
if (ListGetSize(g_aclList) > 0) {
if (BTM_IsControllerSupportRoleSwitch()) {
role = HCI_ROLE_MASTER;
}
}
ListAddLast(g_aclList, connection);
HciAcceptConnectionReqestParam acceptParam = {
.bdAddr = eventParam->bdAddr,
.role = role,
};
int result = HCI_AcceptConnectionRequest(&acceptParam);
if (result != BT_SUCCESS) {
ListRemoveNode(g_aclList, connection);
}
MutexUnlock(g_aclListLock);
}
static void BtmConvertAddressForConnection(BtAddr *addr)
{
bool localResolve = false;
BtAddr address;
if (BTM_IsControllerSupportLlPrivacy()) {
if (BTM_IsDeviceInResolvingList(addr)) {
int result = BTM_GetRemoteIdentityAddress(addr, &address);
if (result == BT_SUCCESS) {
*addr = address;
}
} else {
localResolve = true;
}
} else {
localResolve = true;
}
if (localResolve) {
int result = BTM_GetCurrentRemoteAddress(addr, &address);
if (result == BT_SUCCESS) {
*addr = address;
}
}
}
static int BtmLeExtendedCreateConnection()
{
uint8_t ownAddressType = LOCAL_ADDR_TYPE_PUBLIC;
if (BTM_GetOwnAddressType() == OWN_ADDRESS_TYPE_RANDOM) {
ownAddressType = BTM_IsControllerSupportLlPrivacy() ? LOCAL_ADDR_TYPE_RPA_OR_RANDOM : LOCAL_ADDR_TYPE_RANDOM;
}
uint8_t countOfSets = 1;
uint8_t initiatingPhys = LE_1M_PHY;
if (BTM_IsControllerSupportLe2MPhy()) {
initiatingPhys |= LE_2M_PHY;
countOfSets++;
}
if (BTM_IsControllerSupportLeCodedPhy()) {
initiatingPhys |= LE_CODED_PHY;
countOfSets++;
}
uint16_t leScanInterval = g_leScanInterval;
uint16_t leScanWindow = g_leScanWindow;
HciLeConnectionParamSet *sets = MEM_MALLOC.alloc(sizeof(HciLeConnectionParamSet) * countOfSets);
if (sets == NULL) {
LOG_ERROR("sets is NULL");
return BT_NO_MEMORY;
}
for (uint8_t i = 0; i < countOfSets; i++) {
sets[i].scanInterval = leScanInterval;
sets[i].scanWindow = leScanWindow;
sets[i].connIntervalMin = LE_CONN_INTERVAL_MIN_DEFAULT;
sets[i].connIntervalMax = LE_CONN_INTERVAL_MAX_DEFAULT;
sets[i].connLatency = LE_CONN_LATENCY_DEFAULT;
sets[i].supervisionTimeout = LE_SUPERVISION_TIMEOUT_DEFAULT;
sets[i].minimumCELength = LE_MINIMUM_CE_LENGTH_DEFAULT;
sets[i].maximumCELength = LE_MAXIMUM_CE_LENGTH_DEFAULT;
}
HciLeExtendedCreateConnectionParam param = {
.initiatingFilterPolicy = INITIATOR_FILTER_USE_WHITE_LIST,
.peerAddressType = 0,
.peerAddress = {.raw = {0}},
.ownAddressType = ownAddressType,
.initiatingPhys = initiatingPhys,
.sets = sets,
};
int result = HCI_LeExtendedCreateConnection(¶m);
MEM_MALLOC.free(sets);
return result;
}
static int BtmStartAutoConnectionInternal()
{
if (BTM_IsControllerSupportLeExtendedAdvertising()) {
return BtmLeExtendedCreateConnection();
} else {
uint8_t ownAddressType = LOCAL_ADDR_TYPE_PUBLIC;
if (BTM_GetOwnAddressType() == OWN_ADDRESS_TYPE_RANDOM) {
ownAddressType =
BTM_IsControllerSupportLlPrivacy() ? LOCAL_ADDR_TYPE_RPA_OR_RANDOM : LOCAL_ADDR_TYPE_RANDOM;
}
uint16_t leScanInterval = g_leScanInterval;
uint16_t leScanWindow = g_leScanWindow;
HciLeCreateConnectionParam param = {
.leScanInterval = leScanInterval,
.leScanWindow = leScanWindow,
.initiatorFilterPolicy = INITIATOR_FILTER_USE_WHITE_LIST,
.peerAddressType = 0,
.peerAddress =
{
.raw = {0},
},
.ownAddressType = ownAddressType,
.connIntervalMin = LE_CONN_INTERVAL_MIN_DEFAULT,
.connIntervalMax = LE_CONN_INTERVAL_MAX_DEFAULT,
.connLatency = LE_CONN_LATENCY_DEFAULT,
.supervisionTimeout = LE_SUPERVISION_TIMEOUT_DEFAULT,
.minimumCELength = LE_MINIMUM_CE_LENGTH_DEFAULT,
.maximumCELength = LE_MAXIMUM_CE_LENGTH_DEFAULT,
};
return HCI_LeCreateConnection(¶m);
}
}
int BtmStartAutoConnection()
{
int result;
MutexLock(g_autoConnectLock);
if (BtmGetDeviceCountInWhiteList() > 0) {
result = BtmStartAutoConnectionInternal();
} else {
result = BT_SUCCESS;
}
MutexUnlock(g_autoConnectLock);
return result;
}
static int BtmStopAutoConnectionInternal(void)
{
return HCI_LeCreateConnectionCancel();
}
int BtmStopAutoConnection()
{
int result;
MutexLock(g_autoConnectLock);
if (BtmGetDeviceCountInWhiteList() > 0) {
result = BtmStopAutoConnectionInternal();
} else {
result = BT_SUCCESS;
}
MutexUnlock(g_autoConnectLock);
return result;
}
static void BtmUpdateWhiteList()
{
BtmWhiteListPendingAction *action = QueueTryDequeue(g_leWhiteListPendingActionQueue);
while (action != NULL) {
uint8_t whiteListAddressType = (action->addr.type == BT_PUBLIC_DEVICE_ADDRESS) ? WHITE_LIST_ADDRESS_TYPE_PUBLIC
: WHITE_LIST_ADDRESS_TYPE_RANDOM;
if (action->action == ACTION_ADD_TO_WHITE_LIST) {
BtmAddDeviceToWhiteList(whiteListAddressType, &action->addr);
} else if (action->action == ACTION_REMOVE_FORM_WHITE_LIST) {
BtmRemoveDeviceFromWhiteList(whiteListAddressType, &action->addr);
}
MEM_MALLOC.free(action);
action = QueueTryDequeue(g_leWhiteListPendingActionQueue);
}
}
static int BtmLeCreateConnection(const BtAddr *addr)
{
MutexLock(g_autoConnectLock);
if (BtmGetDeviceCountInWhiteList() > 0) {
BtmWhiteListPendingAction *action = MEM_MALLOC.alloc(sizeof(BtmWhiteListPendingAction));
if (action != NULL) {
action->action = ACTION_ADD_TO_WHITE_LIST;
action->addr = *addr;
QueueEnqueue(g_leWhiteListPendingActionQueue, action);
}
MutexUnlock(g_autoConnectLock);
return BtmStopAutoConnectionInternal();
} else {
uint8_t whiteListAddressType =
(addr->type == BT_PUBLIC_DEVICE_ADDRESS) ? WHITE_LIST_ADDRESS_TYPE_PUBLIC : WHITE_LIST_ADDRESS_TYPE_RANDOM;
BtmAddDeviceToWhiteList(whiteListAddressType, addr);
MutexUnlock(g_autoConnectLock);
return BtmStartAutoConnectionInternal();
}
}
int BTM_LeConnect(const BtAddr *addr)
{
if (addr == NULL) {
return BT_BAD_PARAM;
}
if (!IS_INITIALIZED()) {
return BT_BAD_STATUS;
}
int result = BT_SUCCESS;
BtAddr leAddr = *addr;
BtmConvertAddressForConnection(&leAddr);
MutexLock(g_aclListLock);
if (ListGetSize(g_aclList) >= BTM_MAX_ACL) {
MutexUnlock(g_aclListLock);
return BT_CONNECT_NUM_MAX;
}
BtmAclConnection *connection = BtmAclFindLeConnectionByAddr(addr);
if (connection != NULL) {
if (connection->state != CONNECTING) {
result = BT_BAD_STATUS;
}
} else {
connection = AllocAclConnection();
if (connection != NULL) {
connection->addr = *addr;
connection->transport = TRANSPORT_LE_STACK;
connection->isInitiator = true;
connection->state = CONNECTING;
if (BTM_GetOwnAddressType() == OWN_ADDRESS_TYPE_RANDOM) {
BTM_GetLeRandomAddress(&connection->leLocalAddr);
} else {
BTM_GetLocalAddr(&connection->leLocalAddr);
}
connection->lePeerAddr = leAddr;
ListAddLast(g_aclList, connection);
}
result = BtmLeCreateConnection(&leAddr);
}
MutexUnlock(g_aclListLock);
return result;
}
static void BtmConvertAddressForConnectionComplete(BtAddr *addr)
{
BtAddr pairedAddress;
int result = BTM_ConvertToPairedAddress(addr, &pairedAddress);
if (result == BT_SUCCESS) {
*addr = pairedAddress;
}
}
static void BtmRemoveAllConnectingLeConnection()
{
BtmAclConnection *connection = NULL;
ListNode *node = ListGetFirstNode(g_aclList);
while (node != NULL) {
connection = ListGetNodeData(node);
node = ListGetNextNode(node);
if (connection->transport == TRANSPORT_LE_STACK && connection->state == CONNECTING) {
ListRemoveNode(g_aclList, connection);
}
}
}
static void BtmUpdateLeConnectionOnConnectComplete(
const BtAddr *addr, uint8_t peerAddrType, const HciLeConnectionCompleteEventParam *eventParam)
{
MutexLock(g_aclListLock);
if (eventParam->status == HCI_SUCCESS) {
BtmAclConnection *connection = BtmAclFindLeConnectionByAddr(addr);
if (connection != NULL) {
connection->connectionHandle = eventParam->connectionHandle;
connection->state = CONNECTED;
(void)memcpy_s(connection->lePeerAddr.addr, BT_ADDRESS_SIZE, eventParam->peerAddress.raw, BT_ADDRESS_SIZE);
connection->lePeerAddr.type = peerAddrType;
} else {
connection = AllocAclConnection();
if (connection == NULL) {
MutexUnlock(g_aclListLock);
return;
}
connection->addr = *addr;
connection->connectionHandle = eventParam->connectionHandle;
connection->transport = TRANSPORT_LE_STACK;
connection->state = CONNECTED;
connection->isInitiator = false;
if (BTM_GetOwnAddressType() == OWN_ADDRESS_TYPE_RANDOM) {
BTM_GetLeRandomAddress(&connection->leLocalAddr);
} else {
BTM_GetLocalAddr(&connection->leLocalAddr);
}
(void)memcpy_s(connection->lePeerAddr.addr, BT_ADDRESS_SIZE, eventParam->peerAddress.raw, BT_ADDRESS_SIZE);
connection->lePeerAddr.type = peerAddrType;
ListAddLast(g_aclList, connection);
}
} else if (eventParam->status == HCI_UNKNOWN_CONNECTION_IDENTIFIER) {
} else if (eventParam->status == HCI_CONNECTION_ALREADY_EXISTS) {
} else {
BtmRemoveAllConnectingLeConnection();
}
MutexUnlock(g_aclListLock);
}
NO_SANITIZE("cfi") static void BtmLeCancelConnectCallback(uint8_t status)
{
MutexLock(g_leConnectionCancelLock);
ListNode *node1 = ListGetFirstNode(g_leConnectionCancelList);
BtAddr *addr = NULL;
ListNode *node2 = NULL;
BtmAclCallbacksBlock *block = NULL;
while (node1 != NULL) {
addr = ListGetNodeData(node1);
MutexLock(g_aclCallbackListLock);
node2 = ListGetFirstNode(g_aclCallbackList);
while (node2 != NULL) {
block = (BtmAclCallbacksBlock *)ListGetNodeData(node2);
if (block->callbacks->leConnectionComplete != NULL) {
block->callbacks->leConnectionComplete(status, 0, addr, 0, block->context);
}
node2 = ListGetNextNode(node2);
}
MutexUnlock(g_aclCallbackListLock);
node1 = ListGetNextNode(node1);
}
ListClear(g_leConnectionCancelList);
MutexUnlock(g_leConnectionCancelLock);
}
static void BtmLeReadRemoteFeatures(uint16_t connectionHandle)
{
HciLeReadRemoteFeaturesParam cmdParam = {
.connectionHandle = connectionHandle,
};
HCI_LeReadRemoteFeatures(&cmdParam);
}
static void BtmLeSetDataLength(uint16_t connectionHandle)
{
if (!BTM_IsControllerSupportLeDataPacketLengthExtension()) {
return;
}
uint16_t maxTxOctets = 0;
uint16_t maxTxTime = 0;
uint16_t maxRxOctets = 0;
uint16_t maxRxTime = 0;
if (BTM_GetLeMaxDataLength(&maxTxOctets, &maxTxTime, &maxRxOctets, &maxRxTime) != BT_SUCCESS) {
return;
}
HciLeSetDataLengthParam cmdParam = {
.connectionHandle = connectionHandle,
.txOctets = maxTxOctets,
.txTime = maxTxTime,
};
int ret = HCI_LeSetDataLength(&cmdParam);
if (ret != BT_SUCCESS) {
LOG_WARN("%{public}s: HCI_LeSetDataLength failed:%{public}d", __FUNCTION__, ret);
}
}
NO_SANITIZE("cfi") static void BtmOnLeConnectCallback(
const BtAddr *addrList, uint8_t addrCount, uint8_t status, uint16_t connectionHandle, uint16_t role)
{
MutexLock(g_aclCallbackListLock);
ListNode *node = NULL;
BtmAclCallbacksBlock *block = NULL;
for (uint8_t i = 0; i < addrCount; i++) {
node = ListGetFirstNode(g_aclCallbackList);
while (node != NULL) {
block = (BtmAclCallbacksBlock *)ListGetNodeData(node);
if (block->callbacks->leConnectionComplete != NULL) {
block->callbacks->leConnectionComplete(status, connectionHandle, addrList + i, role, block->context);
}
node = ListGetNextNode(node);
}
}
MutexUnlock(g_aclCallbackListLock);
}
static void BtmGetLeConnectingAddr(BtAddr **addrList, uint8_t *addrCount)
{
MutexLock(g_aclListLock);
uint8_t count = 0;
BtmAclConnection *connection = NULL;
ListNode *node = ListGetFirstNode(g_aclList);
while (node != NULL) {
connection = ListGetNodeData(node);
if ((connection->transport == TRANSPORT_LE_STACK) && (connection->state == CONNECTING)) {
count++;
}
connection = NULL;
node = ListGetNextNode(node);
}
if (count > 0) {
*addrList = MEM_CALLOC.alloc(sizeof(BtAddr) * count);
*addrCount = count;
}
if (*addrList != NULL) {
uint8_t index = 0;
node = ListGetFirstNode(g_aclList);
while (node != NULL) {
connection = ListGetNodeData(node);
if (connection->transport == TRANSPORT_LE_STACK && connection->state == CONNECTING) {
(*addrList)[index] = connection->addr;
index++;
}
connection = NULL;
node = ListGetNextNode(node);
}
}
MutexUnlock(g_aclListLock);
}
static bool IsZeroAddress(const uint8_t addr[BT_ADDRESS_SIZE])
{
bool isZeroAddress = true;
for (uint8_t i = 0; i < BT_ADDRESS_SIZE; i++) {
if (addr[i] != 0) {
isZeroAddress = false;
break;
}
}
return isZeroAddress;
}
static void BtmUpdateUpdateWhiteListOnLeConnectionComplete(
const BtAddr *addrList, const uint8_t addrCount, const HciLeConnectionCompleteEventParam *eventParam)
{
MutexLock(g_autoConnectLock);
BtAddr addr = {
.addr = {0},
.type = (eventParam->peerAddressType == HCI_PEER_ADDR_TYPE_PUBLIC) ? BT_PUBLIC_DEVICE_ADDRESS
: BT_RANDOM_DEVICE_ADDRESS,
};
(void)memcpy_s(addr.addr, BT_ADDRESS_SIZE, eventParam->peerAddress.raw, BT_ADDRESS_SIZE);
bool restartAutoConnection = false;
if (eventParam->status == HCI_SUCCESS) {
uint8_t whiteListAddrType = (eventParam->peerAddressType == HCI_PEER_ADDR_TYPE_PUBLIC)
? WHITE_LIST_ADDRESS_TYPE_PUBLIC
: WHITE_LIST_ADDRESS_TYPE_RANDOM;
restartAutoConnection = BtmIsDeviceInWhiteList(whiteListAddrType, &addr);
} else if (eventParam->status == HCI_UNKNOWN_CONNECTION_IDENTIFIER) {
restartAutoConnection = true;
} else if (eventParam->status == HCI_CONNECTION_ALREADY_EXISTS) {
restartAutoConnection = true;
}
BtmUpdateWhiteList();
if ((eventParam->status == HCI_SUCCESS) || (eventParam->status == HCI_CONNECTION_ALREADY_EXISTS)) {
uint8_t addrType =
(addr.type == BT_PUBLIC_DEVICE_ADDRESS) ? WHITE_LIST_ADDRESS_TYPE_PUBLIC : WHITE_LIST_ADDRESS_TYPE_RANDOM;
BtmRemoveDeviceFromWhiteList(addrType, &addr);
} else if (eventParam->status == HCI_UNKNOWN_CONNECTION_IDENTIFIER) {
} else {
for (uint8_t i = 0; i < addrCount; i++) {
uint8_t addrType = ((addrList + i)->type == BT_PUBLIC_DEVICE_ADDRESS) ? WHITE_LIST_ADDRESS_TYPE_PUBLIC
: WHITE_LIST_ADDRESS_TYPE_RANDOM;
BtmRemoveDeviceFromWhiteList(addrType, addrList + i);
}
}
if (BtmGetDeviceCountInWhiteList() > 0 && restartAutoConnection) {
BtmStartAutoConnection();
}
MutexUnlock(g_autoConnectLock);
}
static void BtmOnLeConnectionComplete(const HciLeConnectionCompleteEventParam *eventParam)
{
uint8_t peerAddrType = (eventParam->peerAddressType == HCI_PEER_ADDR_TYPE_PUBLIC) ? BT_PUBLIC_DEVICE_ADDRESS
: BT_RANDOM_DEVICE_ADDRESS;
BtAddr addr = {
.addr = {0},
.type = peerAddrType,
};
(void)memcpy_s(addr.addr, BT_ADDRESS_SIZE, eventParam->peerAddress.raw, BT_ADDRESS_SIZE);
if (!IsZeroAddress(addr.addr)) {
BtmConvertAddressForConnectionComplete(&addr);
}
BtAddr *addrList = NULL;
uint8_t addrCount = 0;
BtmGetLeConnectingAddr(&addrList, &addrCount);
BtmUpdateLeConnectionOnConnectComplete(&addr, peerAddrType, eventParam);
if (eventParam->status == HCI_SUCCESS) {
BtmLeReadRemoteFeatures(eventParam->connectionHandle);
BtmLeSetDataLength(eventParam->connectionHandle);
}
BtmUpdateUpdateWhiteListOnLeConnectionComplete(addrList, addrCount, eventParam);
switch (eventParam->status) {
case HCI_SUCCESS:
BtmOnLeConnectCallback(&addr, 1, eventParam->status, eventParam->connectionHandle, eventParam->role);
break;
case HCI_UNKNOWN_CONNECTION_IDENTIFIER:
BtmLeCancelConnectCallback(eventParam->status);
break;
case HCI_CONNECTION_ALREADY_EXISTS:
break;
default:
BtmOnLeConnectCallback(
addrList, addrCount, eventParam->status, eventParam->connectionHandle, eventParam->role);
break;
}
if (addrList != NULL) {
MEM_CALLOC.free(addrList);
}
}
static void BtmUpdateLeConnectionOnEnhancedConnectComplete(
BtmAclConnection *connection, uint8_t peerAddrType, const HciLeEnhancedConnectionCompleteEventParam *eventParam)
{
connection->connectionHandle = eventParam->connectionHandle;
connection->state = CONNECTED;
if (!IsZeroAddress(eventParam->localResolvablePrivateAddress.raw)) {
(void)memcpy_s(connection->leLocalAddr.addr,
BT_ADDRESS_SIZE,
eventParam->localResolvablePrivateAddress.raw,
BT_ADDRESS_SIZE);
connection->leLocalAddr.type = BT_RANDOM_DEVICE_ADDRESS;
}
if (!IsZeroAddress(eventParam->peerResolvablePrivateAddress.raw)) {
(void)memcpy_s(connection->lePeerAddr.addr,
BT_ADDRESS_SIZE,
eventParam->peerResolvablePrivateAddress.raw,
BT_ADDRESS_SIZE);
connection->lePeerAddr.type = BT_RANDOM_DEVICE_ADDRESS;
}
}
static void BtmAllocLeConnectionOnEnhancedConnectComplete(
const BtAddr *addr, uint8_t peerAddrType, const HciLeEnhancedConnectionCompleteEventParam *eventParam)
{
BtmAclConnection *connection = AllocAclConnection();
if (connection == NULL) {
return;
}
connection->addr = *addr;
connection->connectionHandle = eventParam->connectionHandle;
connection->transport = TRANSPORT_LE_STACK;
connection->state = CONNECTED;
connection->isInitiator = false;
if (!IsZeroAddress(eventParam->localResolvablePrivateAddress.raw)) {
(void)memcpy_s(connection->leLocalAddr.addr,
BT_ADDRESS_SIZE,
eventParam->localResolvablePrivateAddress.raw,
BT_ADDRESS_SIZE);
connection->leLocalAddr.type = BT_RANDOM_DEVICE_ADDRESS;
} else {
if (BTM_GetOwnAddressType() == OWN_ADDRESS_TYPE_RANDOM) {
BTM_GetLeRandomAddress(&connection->leLocalAddr);
} else {
BTM_GetLocalAddr(&connection->leLocalAddr);
}
}
if (!IsZeroAddress(eventParam->peerResolvablePrivateAddress.raw)) {
(void)memcpy_s(connection->lePeerAddr.addr,
BT_ADDRESS_SIZE,
eventParam->peerResolvablePrivateAddress.raw,
BT_ADDRESS_SIZE);
connection->lePeerAddr.type = BT_RANDOM_DEVICE_ADDRESS;
} else {
(void)memcpy_s(connection->lePeerAddr.addr, BT_ADDRESS_SIZE, eventParam->peerAddress.raw, BT_ADDRESS_SIZE);
connection->lePeerAddr.type = peerAddrType;
}
ListAddLast(g_aclList, connection);
}
static void BtmUpdateConnectionInfoOnLeEnhancedConnectionComplete(
const BtAddr *addr, uint8_t peerAddrType, const HciLeEnhancedConnectionCompleteEventParam *eventParam)
{
MutexLock(g_aclListLock);
if (eventParam->status == HCI_SUCCESS) {
BtmAclConnection *connection = BtmAclFindLeConnectionByAddr(addr);
if (connection != NULL) {
BtmUpdateLeConnectionOnEnhancedConnectComplete(connection, peerAddrType, eventParam);
} else {
BtmAllocLeConnectionOnEnhancedConnectComplete(addr, peerAddrType, eventParam);
}
} else if (eventParam->status == HCI_UNKNOWN_CONNECTION_IDENTIFIER) {
} else if (eventParam->status == HCI_CONNECTION_ALREADY_EXISTS) {
} else {
BtmRemoveAllConnectingLeConnection();
}
MutexUnlock(g_aclListLock);
}
static void BtmUpdateWhiteListOnLeEnhancedConnectionComplete(
const BtAddr *addrList, const uint8_t addrCount, const HciLeEnhancedConnectionCompleteEventParam *eventParam)
{
MutexLock(g_autoConnectLock);
BtAddr addr = {
.addr = {0},
.type = (eventParam->peerAddressType == HCI_PEER_ADDR_TYPE_PUBLIC) ? BT_PUBLIC_DEVICE_ADDRESS
: BT_RANDOM_DEVICE_ADDRESS,
};
(void)memcpy_s(addr.addr, BT_ADDRESS_SIZE, eventParam->peerAddress.raw, BT_ADDRESS_SIZE);
bool restartAutoConnection = false;
if (eventParam->status == HCI_SUCCESS) {
uint8_t whiteListAddrType = (eventParam->peerAddressType == HCI_PEER_ADDR_TYPE_PUBLIC)
? WHITE_LIST_ADDRESS_TYPE_PUBLIC
: WHITE_LIST_ADDRESS_TYPE_RANDOM;
restartAutoConnection = BtmIsDeviceInWhiteList(whiteListAddrType, &addr);
} else if (eventParam->status == HCI_UNKNOWN_CONNECTION_IDENTIFIER) {
restartAutoConnection = true;
} else if (eventParam->status == HCI_CONNECTION_ALREADY_EXISTS) {
restartAutoConnection = true;
}
BtmUpdateWhiteList();
if ((eventParam->status == HCI_SUCCESS) || (eventParam->status == HCI_CONNECTION_ALREADY_EXISTS)) {
uint8_t addrType =
(addr.type == BT_PUBLIC_DEVICE_ADDRESS) ? WHITE_LIST_ADDRESS_TYPE_PUBLIC : WHITE_LIST_ADDRESS_TYPE_RANDOM;
BtmRemoveDeviceFromWhiteList(addrType, &addr);
} else if (eventParam->status == HCI_UNKNOWN_CONNECTION_IDENTIFIER) {
} else {
for (uint8_t i = 0; i < addrCount; i++) {
uint8_t addrType = ((addrList + i)->type == BT_PUBLIC_DEVICE_ADDRESS) ? WHITE_LIST_ADDRESS_TYPE_PUBLIC
: WHITE_LIST_ADDRESS_TYPE_RANDOM;
BtmRemoveDeviceFromWhiteList(addrType, addrList + i);
}
}
if (BtmGetDeviceCountInWhiteList() > 0 && restartAutoConnection) {
BtmStartAutoConnectionInternal();
}
MutexUnlock(g_autoConnectLock);
}
static void BtmOnLeEnhancedConnectionComplete(const HciLeEnhancedConnectionCompleteEventParam *eventParam)
{
uint8_t peerAddrType = (eventParam->peerAddressType == HCI_PEER_ADDR_TYPE_PUBLIC) ? BT_PUBLIC_DEVICE_ADDRESS
: BT_RANDOM_DEVICE_ADDRESS;
BtAddr addr = {
.addr = {0},
.type = peerAddrType,
};
(void)memcpy_s(addr.addr, BT_ADDRESS_SIZE, eventParam->peerAddress.raw, BT_ADDRESS_SIZE);
if (!IsZeroAddress(addr.addr)) {
BtmConvertAddressForConnectionComplete(&addr);
}
BtAddr *addrList = NULL;
uint8_t addrCount = 0;
BtmGetLeConnectingAddr(&addrList, &addrCount);
BtmUpdateConnectionInfoOnLeEnhancedConnectionComplete(&addr, peerAddrType, eventParam);
if (eventParam->status == HCI_SUCCESS) {
BtmLeReadRemoteFeatures(eventParam->connectionHandle);
BtmLeSetDataLength(eventParam->connectionHandle);
}
BtmUpdateWhiteListOnLeEnhancedConnectionComplete(addrList, addrCount, eventParam);
switch (eventParam->status) {
case HCI_SUCCESS:
BtmOnLeConnectCallback(&addr, 1, eventParam->status, eventParam->connectionHandle, eventParam->role);
break;
case HCI_UNKNOWN_CONNECTION_IDENTIFIER:
BtmLeCancelConnectCallback(eventParam->status);
break;
case HCI_CONNECTION_ALREADY_EXISTS:
break;
default:
BtmOnLeConnectCallback(
addrList, addrCount, eventParam->status, eventParam->connectionHandle, eventParam->role);
break;
}
if (addrList != NULL) {
MEM_CALLOC.free(addrList);
}
}
uint8_t BTM_GetAclTranspot(uint16_t connectionHandle)
{
if (!IS_INITIALIZED()) {
return -1;
}
uint8_t transport = -1;
MutexLock(g_aclListLock);
BtmAclConnection *connection = BtmAclFindConnectionByHandle(connectionHandle);
if (connection != NULL) {
transport = connection->transport;
}
MutexUnlock(g_aclListLock);
return transport;
}
int BTM_AclDisconnect(uint16_t connectionHandle, uint8_t reason)
{
if (!IS_INITIALIZED()) {
return BT_BAD_STATUS;
}
int result = BT_SUCCESS;
MutexLock(g_aclListLock);
BtmAclConnection *connection = BtmAclFindConnectionByHandle(connectionHandle);
if (connection != NULL) {
if (connection->state != DISCONNECTING) {
connection->state = DISCONNECTING;
HciDisconnectParam param = {
.connectionHandle = connectionHandle,
.reason = reason,
};
result = HCI_Disconnect(¶m);
}
} else {
result = BT_BAD_STATUS;
}
MutexUnlock(g_aclListLock);
return result;
}
NO_SANITIZE("cfi") static void BtmOnDisconnectComplete(const HciDisconnectCompleteEventParam *eventParam)
{
uint8_t transport = 0;
MutexLock(g_aclListLock);
BtmAclConnection *connection = BtmAclFindConnectionByHandle(eventParam->connectionHandle);
if (connection != NULL) {
transport = connection->transport;
if (eventParam->status == HCI_SUCCESS) {
connection->state = DISCONNECTED;
ListRemoveNode(g_aclList, connection);
connection = NULL;
}
}
MutexUnlock(g_aclListLock);
MutexLock(g_aclCallbackListLock);
ListNode *node = ListGetFirstNode(g_aclCallbackList);
BtmAclCallbacksBlock *block = NULL;
while (node != NULL) {
block = (BtmAclCallbacksBlock *)ListGetNodeData(node);
if (transport == TRANSPORT_BREDR_STACK) {
if (block->callbacks->disconnectionComplete != NULL) {
block->callbacks->disconnectionComplete(
eventParam->status, eventParam->connectionHandle, eventParam->reason, block->context);
}
} else if (transport == TRANSPORT_LE_STACK) {
if (block->callbacks->leDisconnectionComplete != NULL) {
block->callbacks->leDisconnectionComplete(
eventParam->status, eventParam->connectionHandle, eventParam->reason, block->context);
}
}
node = ListGetNextNode(node);
}
MutexUnlock(g_aclCallbackListLock);
}
static void BtmOnReadRssiComplete(const HciReadRssiReturnParam *returnParam)
{
BtAddr addr;
MutexLock(g_aclListLock);
BtmAclConnection *connection = BtmAclFindConnectionByHandle(returnParam->handle);
if (connection != NULL) {
addr = connection->addr;
}
MutexUnlock(g_aclListLock);
MutexLock(g_aclCallbackListLock);
BtmAclCallbacksBlock *block = NULL;
ListNode *node = ListGetFirstNode(g_aclCallbackList);
while (node != NULL) {
block = ListGetNodeData(node);
if (block->callbacks->readRssiComplete != NULL) {
block->callbacks->readRssiComplete(returnParam->status, &addr, returnParam->rssi, block->context);
}
node = ListGetNextNode(node);
}
MutexUnlock(g_aclCallbackListLock);
}
static void BtmAclOnCommandStatus(uint8_t status, uint16_t commandOpcode)
{
if (commandOpcode == HCI_DISCONNECT) {
MutexLock(g_cleanupMutex);
if (g_cleanupHandle != 0xffff) {
AlarmCancel(g_cleanupTimer);
SemaphorePost(g_cleanupEvent);
}
MutexUnlock(g_cleanupMutex);
}
}
int BTM_RegisterAclCallbacks(const BtmAclCallbacks *callbacks, void *context)
{
if (callbacks == NULL) {
return BT_BAD_PARAM;
}
if (!IS_INITIALIZED()) {
return BT_BAD_STATUS;
}
BtmAclCallbacksBlock *block = BtmAllocAclCallbacksBlock(callbacks, context);
if (block == NULL) {
return BT_NO_MEMORY;
}
MutexLock(g_aclCallbackListLock);
ListAddLast(g_aclCallbackList, block);
MutexUnlock(g_aclCallbackListLock);
return BT_SUCCESS;
}
int BTM_DeregisterAclCallbacks(const BtmAclCallbacks *callbacks)
{
if (callbacks == NULL) {
return BT_BAD_PARAM;
}
if (!IS_INITIALIZED()) {
return BT_BAD_STATUS;
}
MutexLock(g_aclCallbackListLock);
BtmAclCallbacksBlock *block = NULL;
ListNode *node = ListGetFirstNode(g_aclCallbackList);
while (node != NULL) {
block = ListGetNodeData(node);
if (block->callbacks == callbacks) {
ListRemoveNode(g_aclCallbackList, block);
break;
}
node = ListGetNextNode(node);
}
MutexUnlock(g_aclCallbackListLock);
return BT_SUCCESS;
}
int BtmGetAclHandleByAddress(const BtAddr *addr, uint16_t *handle)
{
int result = BT_SUCCESS;
MutexLock(g_aclListLock);
BtmAclConnection *connection = BtmAclFindConnectionByAddr(addr);
if (connection != NULL) {
*handle = connection->connectionHandle;
} else {
result = BT_BAD_STATUS;
}
MutexUnlock(g_aclListLock);
return result;
}
int BtmGetLeAclHandleByAddress(const BtAddr *addr, uint16_t *handle)
{
int result = BT_SUCCESS;
MutexLock(g_aclListLock);
BtmAclConnection *connection = BtmAclFindLeConnectionByAddr(addr);
if (connection != NULL) {
*handle = connection->connectionHandle;
} else {
result = BT_BAD_STATUS;
}
MutexUnlock(g_aclListLock);
return result;
}
int BtmGetAclAddressByHandle(const uint16_t handle, BtAddr *addr)
{
int result = BT_SUCCESS;
MutexLock(g_aclListLock);
BtmAclConnection *connection = BtmAclFindConnectionByHandle(handle);
if (connection != NULL) {
*addr = connection->addr;
} else {
result = BT_BAD_STATUS;
}
MutexUnlock(g_aclListLock);
return result;
}
int BTM_AclAddRef(uint16_t connectionHandle)
{
if (!IS_INITIALIZED()) {
return BT_OPERATION_FAILED;
}
int result = BT_SUCCESS;
MutexLock(g_aclListLock);
BtmAclConnection *connection = BtmAclFindConnectionByHandle(connectionHandle);
if (connection != NULL) {
if (connection->state == DISCONNECTING) {
result = BT_BAD_STATUS;
} else {
connection->refCount++;
AlarmCancel(connection->timeoutTimer);
}
} else {
result = BT_BAD_PARAM;
}
MutexUnlock(g_aclListLock);
return result;
}
void BtmReleaseConnection(BtmAclConnection *connection)
{
if (connection->isInitiator) {
connection->state = DISCONNECTING;
HciDisconnectParam param = {
.connectionHandle = connection->connectionHandle,
.reason = HCI_REMOTE_USER_TERMINATED_CONNECTION,
};
HCI_Disconnect(¶m);
} else {
if (connection->transport == TRANSPORT_BREDR_STACK) {
AlarmSet(connection->timeoutTimer, ACL_PASSIVE_TIMEOUT, BtmAclTimeout, connection);
} else if (connection->transport == TRANSPORT_LE_STACK) {
connection->state = DISCONNECTING;
HciDisconnectParam param = {
.connectionHandle = connection->connectionHandle,
.reason = HCI_REMOTE_USER_TERMINATED_CONNECTION,
};
HCI_Disconnect(¶m);
}
}
}
void BTM_AclRelease(uint16_t connectionHandle)
{
if (!IS_INITIALIZED()) {
return;
}
MutexLock(g_aclListLock);
BtmAclConnection *connection = BtmAclFindConnectionByHandle(connectionHandle);
if (connection != NULL) {
connection->refCount--;
if (connection->refCount == 0) {
BtmReleaseConnection(connection);
}
}
MutexUnlock(g_aclListLock);
}
int BTM_ReadRssi(const BtAddr *addr)
{
if (addr == NULL) {
return BT_BAD_PARAM;
}
if (!IS_INITIALIZED()) {
return BT_BAD_STATUS;
}
uint16_t handle = 0xffff;
MutexLock(g_aclListLock);
BtmAclConnection *connection = BtmAclFindLeConnectionByAddr(addr);
if (connection != NULL && connection->state == CONNECTED) {
handle = connection->connectionHandle;
} else {
MutexUnlock(g_aclListLock);
return BT_BAD_STATUS;
}
MutexUnlock(g_aclListLock);
HciReadRssiParam param = {
.handle = handle,
};
return HCI_ReadRssi(¶m);
}
int BTM_GetLeConnectionAddress(uint16_t connectionHandle, BtAddr *localAddr, BtAddr *peerAddr)
{
if (!IS_INITIALIZED()) {
return BT_BAD_STATUS;
}
int result = BT_SUCCESS;
MutexLock(g_aclListLock);
BtmAclConnection *connection = BtmAclFindConnectionByHandle(connectionHandle);
if (connection != NULL) {
*localAddr = connection->leLocalAddr;
*peerAddr = connection->lePeerAddr;
} else {
result = BT_BAD_STATUS;
}
MutexUnlock(g_aclListLock);
return result;
}
static void BtmOnEncryptionChange(const HciEncryptionChangeEventParam *eventParam)
{
if (eventParam->status != HCI_SUCCESS) {
return;
}
MutexLock(g_aclListLock);
BtmAclConnection *connection = BtmAclFindConnectionByHandle(eventParam->connectionHandle);
if (connection != NULL) {
connection->encryption = eventParam->encryptionEnabled;
}
MutexUnlock(g_aclListLock);
}
bool BTM_IsSecureConnection(const BtAddr *addr)
{
if (!IS_INITIALIZED()) {
return false;
}
bool isSecureConnection = false;
MutexLock(g_aclListLock);
BtmAclConnection *connection = BtmAclFindConnectionByAddr(addr);
if (connection != NULL) {
if (connection->encryption == LINK_LEVEL_ENCRYPTION_ON_AES_CCM) {
isSecureConnection = true;
}
}
MutexUnlock(g_aclListLock);
return isSecureConnection;
}
static void BtmAclOnCleanUpTimeout(void *parameter)
{
SemaphorePost(g_cleanupEvent);
}
void BtmCloseAclConnectionByTransport(uint8_t transport)
{
bool noActiveConnection = false;
BtmAclConnection *connection = NULL;
HciDisconnectParam param = {
.reason = HCI_REMOTE_USER_TERMINATED_CONNECTION,
};
while (!noActiveConnection) {
uint16_t connectionHandle = 0xffff;
MutexLock(g_aclListLock);
ListNode *node = ListGetFirstNode(g_aclList);
while (node != NULL) {
connection = ListGetNodeData(node);
if (connection->transport == transport && connection->state == CONNECTED) {
connectionHandle = connection->connectionHandle;
connection->state = DISCONNECTING;
break;
}
node = ListGetNextNode(node);
}
MutexUnlock(g_aclListLock);
if (connectionHandle != 0xffff) {
MutexLock(g_cleanupMutex);
g_cleanupHandle = connectionHandle;
MutexUnlock(g_cleanupMutex);
param.connectionHandle = connectionHandle;
HCI_Disconnect(¶m);
AlarmSet(g_cleanupTimer, CLEANUP_TIMEOUT, BtmAclOnCleanUpTimeout, NULL);
SemaphoreWait(g_cleanupEvent);
MutexLock(g_cleanupMutex);
g_cleanupHandle = 0xffff;
MutexUnlock(g_cleanupMutex);
} else {
noActiveConnection = true;
}
}
}
int BTM_AclCancelConnect(const BtAddr *addr)
{
if (addr == NULL) {
return BT_BAD_PARAM;
}
if (!IS_INITIALIZED()) {
return BT_BAD_STATUS;
}
int result;
MutexLock(g_aclListLock);
BtmAclConnection *connection = BtmAclFindConnectionByAddr(addr);
if (connection != NULL) {
ListRemoveNode(g_aclList, connection);
HciCreateConnectionCancelParam param = {
.bdAddr =
{
.raw = {0},
},
};
(void)memcpy_s(param.bdAddr.raw, BT_ADDRESS_SIZE, addr->addr, BT_ADDRESS_SIZE);
result = HCI_CreateConnectionCancel(¶m);
} else {
result = BT_BAD_STATUS;
}
MutexUnlock(g_aclListLock);
return result;
}
int BTM_LeCancelConnect(const BtAddr *addr)
{
if (addr == NULL) {
return BT_BAD_PARAM;
}
if (!IS_INITIALIZED()) {
return BT_BAD_STATUS;
}
int reuslt = BT_SUCCESS;
MutexLock(g_aclListLock);
BtmAclConnection *connection = BtmAclFindLeConnectionByAddr(addr);
if (connection != NULL) {
if (connection->state == CONNECTING) {
MutexLock(g_leConnectionCancelLock);
BtAddr *cancelAddr = MEM_MALLOC.alloc(sizeof(BtAddr));
if (cancelAddr != NULL) {
*cancelAddr = *addr;
ListAddLast(g_leConnectionCancelList, cancelAddr);
}
MutexUnlock(g_leConnectionCancelLock);
BtmWhiteListPendingAction *action = MEM_MALLOC.alloc(sizeof(BtmWhiteListPendingAction));
if (action != NULL) {
action->action = ACTION_REMOVE_FORM_WHITE_LIST;
action->addr = *addr;
BtmConvertAddressForConnection(&action->addr);
QueueEnqueue(g_leWhiteListPendingActionQueue, action);
}
BtmStopAutoConnection();
ListRemoveNode(g_aclList, connection);
} else {
reuslt = BT_BAD_STATUS;
}
} else {
reuslt = BT_BAD_STATUS;
}
MutexUnlock(g_aclListLock);
return reuslt;
}
void BTM_IsRemoteDeviceSupportHostSecureSimplePairing(const BtAddr *addr, BTMRemoteDeviceSupportCallback callback)
{
BtmAclConnection *connection = NULL;
BTMRemoteDeviceSupportCallback callback_ = NULL;
bool isSupported = false;
MutexLock(g_aclListLock);
connection = BtmAclFindConnectionByAddr(addr);
if (connection != NULL) {
if (connection->remoteFeatures.bredr.extendedFeatureStatus != REQUEST_NOT_COMPLETED) {
callback_ = callback;
isSupported =
HCI_SUPPORT_SECURE_SIMPLE_PAIRING_HOST(connection->remoteFeatures.bredr.extendedLmpFeatures.page[1]);
} else {
BtmRemoteDeviceSupportRequest *request = MEM_MALLOC.alloc(sizeof(BtmRemoteDeviceSupportRequest));
if (request != NULL) {
request->addr = *addr;
request->callback = callback;
request->connectionHandle = connection->connectionHandle;
request->event = REMOTE_EXTENDED_FEATURE_COMPLETE;
request->feature.extendedFeature = SECURE_SIMPLE_PAIRING_HOST_SUPPORT;
ListAddLast(g_remoteSupportRequestList, request);
}
}
} else {
callback_ = callback;
}
MutexUnlock(g_aclListLock);
if (callback_ != NULL) {
callback_(addr, isSupported);
}
}
void BTM_IsRemoteDeviceSupportConnectionParametersRequest(const BtAddr *addr, BTMRemoteDeviceSupportCallback callback)
{
BtmAclConnection *connection = NULL;
BTMRemoteDeviceSupportCallback callback_ = NULL;
bool isSupported = false;
MutexLock(g_aclListLock);
connection = BtmAclFindConnectionByAddr(addr);
if (connection != NULL) {
if (connection->remoteFeatures.bredr.extendedFeatureStatus != REQUEST_NOT_COMPLETED) {
callback_ = callback;
isSupported =
HciSupportConnectionParametersRequestProcedure(connection->remoteFeatures.le.leFeatures.raw);
} else {
BtmRemoteDeviceSupportRequest *request = MEM_MALLOC.alloc(sizeof(BtmRemoteDeviceSupportRequest));
if (request != NULL) {
request->addr = *addr;
request->callback = callback;
request->connectionHandle = connection->connectionHandle;
request->event = REMOTE_LE_FEATURE_COMPLETE;
request->feature.leFreature = CONNECTION_PARAMETER_REQUEST;
ListAddLast(g_remoteSupportRequestList, request);
}
}
} else {
callback_ = callback;
}
MutexUnlock(g_aclListLock);
if (callback_ != NULL) {
callback_(addr, isSupported);
}
}
void BTM_IsRemoteDeviceSupportEdrAcl2MbMode(const BtAddr *addr, BTMRemoteDeviceSupportCallback callback)
{
BtmAclConnection *connection = NULL;
BTMRemoteDeviceSupportCallback callback_ = NULL;
bool isSupported = false;
MutexLock(g_aclListLock);
connection = BtmAclFindConnectionByAddr(addr);
if (connection != NULL) {
if (connection->remoteFeatures.bredr.featureStatus != REQUEST_NOT_COMPLETED) {
callback_ = callback;
isSupported = HCI_SUPPORT_EDR_ACL_2MBS_MODE(connection->remoteFeatures.bredr.lmpFeatures.raw);
} else {
BtmRemoteDeviceSupportRequest *request = MEM_MALLOC.alloc(sizeof(BtmRemoteDeviceSupportRequest));
if (request != NULL) {
request->addr = *addr;
request->callback = callback;
request->connectionHandle = connection->connectionHandle;
request->event = REMOTE_FEATURE_COMPLETE;
request->feature.feature = EDR_ACL_2MB_MODE;
ListAddLast(g_remoteSupportRequestList, request);
}
}
} else {
callback_ = callback;
}
MutexUnlock(g_aclListLock);
if (callback_ != NULL) {
callback_(addr, isSupported);
}
}
void BTM_IsRemoteDeviceSupportEdrAcl3MbMode(const BtAddr *addr, BTMRemoteDeviceSupportCallback callback)
{
BtmAclConnection *connection = NULL;
BTMRemoteDeviceSupportCallback callback_ = NULL;
bool isSupported = false;
MutexLock(g_aclListLock);
connection = BtmAclFindConnectionByAddr(addr);
if (connection != NULL) {
if (connection->remoteFeatures.bredr.featureStatus != REQUEST_NOT_COMPLETED) {
callback_ = callback;
isSupported = HCI_SUPPORT_EDR_ACL_3MBS_MODE(connection->remoteFeatures.bredr.lmpFeatures.raw);
} else {
BtmRemoteDeviceSupportRequest *request = MEM_MALLOC.alloc(sizeof(BtmRemoteDeviceSupportRequest));
if (request != NULL) {
request->addr = *addr;
request->callback = callback;
request->connectionHandle = connection->connectionHandle;
request->event = REMOTE_FEATURE_COMPLETE;
request->feature.feature = EDR_ACL_3MB_MODE;
ListAddLast(g_remoteSupportRequestList, request);
}
}
} else {
callback_ = callback;
}
MutexUnlock(g_aclListLock);
if (callback_ != NULL) {
callback_(addr, isSupported);
}
}
int BTM_SetDefaultLinkPolicySettings(uint16_t linkPolicySettings)
{
HciWriteDefaultLinkPolicySettingsParam param = {
.defaultLinkPolicySettings = HCI_LINK_POLICY_DISABLE_ALL,
};
if ((linkPolicySettings & BTM_LINK_POLICY_ENABLE_ROLE_SWITCH) && BTM_IsControllerSupportRoleSwitch()) {
param.defaultLinkPolicySettings |= HCI_LINK_POLICY_ENABLE_ROLE_SWITCH;
}
if ((linkPolicySettings & BTM_LINK_POLICY_ENABLE_HOLD_MODE) && BTM_IsControllerSupportHoldMode()) {
param.defaultLinkPolicySettings |= HCI_LINK_POLICY_ENABLE_HOLD_MODE;
}
if ((linkPolicySettings & BTM_LINK_POLICY_ENABLE_SNIFF_MODE) && BTM_IsControllerSupportSniffMode()) {
param.defaultLinkPolicySettings |= HCI_LINK_POLICY_ENABLE_SNIFF_MODE;
}
return HCI_WriteDefaultLinkPolicySettings(¶m);
}
int BTM_SetLinkPolicySettings(const BtAddr *addr, uint16_t linkPolicySettings)
{
uint16_t connectionHandle = 0xffff;
int result = BtmGetAclHandleByAddress(addr, &connectionHandle);
if (result != BT_SUCCESS) {
return result;
}
HciWriteLinkPolicySettingsParam param = {
.connectionHandle = connectionHandle,
.linkPolicySettings = HCI_LINK_POLICY_DISABLE_ALL,
};
if ((linkPolicySettings & BTM_LINK_POLICY_ENABLE_ROLE_SWITCH) && BTM_IsControllerSupportRoleSwitch()) {
param.linkPolicySettings |= HCI_LINK_POLICY_ENABLE_ROLE_SWITCH;
}
if ((linkPolicySettings & BTM_LINK_POLICY_ENABLE_HOLD_MODE) && BTM_IsControllerSupportHoldMode()) {
param.linkPolicySettings |= HCI_LINK_POLICY_ENABLE_HOLD_MODE;
}
if ((linkPolicySettings & BTM_LINK_POLICY_ENABLE_SNIFF_MODE) && BTM_IsControllerSupportSniffMode()) {
param.linkPolicySettings |= HCI_LINK_POLICY_ENABLE_SNIFF_MODE;
}
return HCI_WriteLinkPolicySettings(¶m);
}
void BtmOnRoleChange(const HciRoleChangeEventParam *eventParam)
{
BtAddr addr = {
.addr = {0},
.type = BT_PUBLIC_DEVICE_ADDRESS,
};
(void)memcpy_s(addr.addr, BT_ADDRESS_SIZE, eventParam->bdAddr.raw, BT_ADDRESS_SIZE);
MutexLock(g_aclCallbackListLock);
ListNode *node = ListGetFirstNode(g_aclCallbackList);
BtmAclCallbacksBlock *block = NULL;
while (node != NULL) {
block = (BtmAclCallbacksBlock *)ListGetNodeData(node);
if (block->callbacks->roleChange != NULL) {
block->callbacks->roleChange(eventParam->status, &addr, eventParam->newRole, block->context);
}
node = ListGetNextNode(node);
}
MutexUnlock(g_aclCallbackListLock);
}
int BTM_SwitchRole(const BtAddr *addr, uint8_t role)
{
if (!IS_INITIALIZED()) {
return BT_BAD_STATUS;
}
if (addr == NULL || (role != BTM_ROLE_MASTER && role != BTM_ROLE_SLAVE)) {
return BT_BAD_PARAM;
}
uint16_t connectionHandle = 0xffff;
int result = BtmGetAclHandleByAddress(addr, &connectionHandle);
if (result != BT_SUCCESS) {
return result;
}
BTM_ExitSniffMode(addr);
HciSwitchRoleParam param = {
.bdAddr =
{
.raw = {0},
},
.role = role,
};
(void)memcpy_s(param.bdAddr.raw, BT_ADDRESS_SIZE, addr->addr, BT_ADDRESS_SIZE);
result = HCI_SwitchRole(¶m);
return result;
}
int BTM_SetLeConnectionModeToFast()
{
if (!IS_INITIALIZED()) {
return BT_BAD_STATUS;
}
MutexLock(g_autoConnectLock);
if (g_leScanInterval != LE_SCAN_INTERVAL_FAST || g_leScanWindow != LE_SCAN_WINDOW_FAST) {
if (BtmGetDeviceCountInWhiteList() > 0) {
BtmStopAutoConnectionInternal();
}
g_leScanInterval = LE_SCAN_INTERVAL_FAST;
g_leScanWindow = LE_SCAN_WINDOW_FAST;
}
MutexUnlock(g_autoConnectLock);
return BT_SUCCESS;
}
int BTM_SetLeConnectionModeToSlow()
{
if (!IS_INITIALIZED()) {
return BT_BAD_STATUS;
}
MutexLock(g_autoConnectLock);
if (g_leScanInterval != LE_SCAN_INTERVAL_SLOW || g_leScanWindow != LE_SCAN_WINDOW_SLOW) {
if (BtmGetDeviceCountInWhiteList() > 0) {
BtmStopAutoConnectionInternal();
}
g_leScanInterval = LE_SCAN_INTERVAL_SLOW;
g_leScanWindow = LE_SCAN_WINDOW_SLOW;
}
MutexUnlock(g_autoConnectLock);
return BT_SUCCESS;
}
static uint16_t BtmGenerateSupportedPacketTypes(const HciLmpFeatures *lmpFeature)
{
uint16_t packetType = BTM_ACL_PACKET_TYPE_DH1 | BTM_ACL_PACKET_TYPE_DM1;
if (HCI_SUPPORT_3_SLOT_PACKETS(lmpFeature->raw)) {
packetType |= BTM_ACL_PACKET_TYPE_DH3;
packetType |= BTM_ACL_PACKET_TYPE_DM3;
}
if (HCI_SUPPORT_5_SLOT_PACKETS(lmpFeature->raw)) {
packetType |= BTM_ACL_PACKET_TYPE_DH5;
packetType |= BTM_ACL_PACKET_TYPE_DM5;
}
if (!HCI_SUPPORT_EDR_ACL_2MBS_MODE(lmpFeature->raw)) {
packetType |= BTM_ACL_PACKET_TYPE_NO_2_DH1;
packetType |= BTM_ACL_PACKET_TYPE_NO_2_DH3;
packetType |= BTM_ACL_PACKET_TYPE_NO_2_DH5;
} else {
if (!HCI_SUPPORT_3_SLOT_EDR_PACKET(lmpFeature->raw)) {
packetType |= BTM_ACL_PACKET_TYPE_NO_2_DH3;
}
if (!HCI_SUPPORT_5_SLOT_EDR_PACKET(lmpFeature->raw)) {
packetType |= BTM_ACL_PACKET_TYPE_NO_2_DH5;
}
}
if (!HCI_SUPPORT_EDR_ACL_3MBS_MODE(lmpFeature->raw)) {
packetType |= BTM_ACL_PACKET_TYPE_NO_3_DH1;
packetType |= BTM_ACL_PACKET_TYPE_NO_3_DH3;
packetType |= BTM_ACL_PACKET_TYPE_NO_3_DH5;
} else {
if (!HCI_SUPPORT_3_SLOT_EDR_PACKET(lmpFeature->raw)) {
packetType |= BTM_ACL_PACKET_TYPE_NO_3_DH3;
}
if (!HCI_SUPPORT_5_SLOT_EDR_PACKET(lmpFeature->raw)) {
packetType |= BTM_ACL_PACKET_TYPE_NO_3_DH5;
}
}
return packetType;
}
int BTM_ChangeConnectionPacketType(const BtAddr *addr, uint16_t packetType)
{
if (!IS_INITIALIZED()) {
return BT_BAD_STATUS;
}
if (addr == NULL) {
return BT_BAD_PARAM;
}
uint16_t connectionHandle = 0xffff;
uint16_t peerPacketType = 0;
MutexLock(g_aclListLock);
BtmAclConnection *connection = BtmAclFindConnectionByAddr(addr);
if (connection != NULL) {
connectionHandle = connection->connectionHandle;
peerPacketType = BtmGenerateSupportedPacketTypes(&connection->remoteFeatures.bredr.lmpFeatures);
}
MutexUnlock(g_aclListLock);
if (connectionHandle == 0xffff) {
return BT_BAD_STATUS;
}
HciLmpFeatures lmpFeatures = {0};
BtmGetLocalSupportedFeature(&lmpFeatures);
uint16_t localPacketType = BtmGenerateSupportedPacketTypes(&lmpFeatures);
const uint16_t usedTypes = BTM_ACL_PACKET_TYPE_DM1 | BTM_ACL_PACKET_TYPE_DH1 | BTM_ACL_PACKET_TYPE_DM3 |
BTM_ACL_PACKET_TYPE_DH3 | BTM_ACL_PACKET_TYPE_DM5 | BTM_ACL_PACKET_TYPE_DH5;
const uint16_t unusedTypes = BTM_ACL_PACKET_TYPE_NO_2_DH1 | BTM_ACL_PACKET_TYPE_NO_3_DH1 |
BTM_ACL_PACKET_TYPE_NO_2_DH3 | BTM_ACL_PACKET_TYPE_NO_3_DH3 |
BTM_ACL_PACKET_TYPE_NO_2_DH5 | BTM_ACL_PACKET_TYPE_NO_3_DH5;
uint16_t hciPacketType = (packetType & usedTypes) & (localPacketType & usedTypes) & (peerPacketType & usedTypes);
hciPacketType |= ((packetType & unusedTypes) | (localPacketType & unusedTypes) | (peerPacketType & unusedTypes));
HciChangeConnectionPacketTypeParam param = {
.connectionHandle = connectionHandle,
.packetType = hciPacketType,
};
return HCI_ChangeConnectionPacketType(¶m);
}
static HciEventCallbacks g_hciEventCallbacks = {
.connectionComplete = BtmOnConnectionComplete,
.connectionIndication = BtmOnConnectionIndication,
.disconnectComplete = BtmOnDisconnectComplete,
.encryptionChange = BtmOnEncryptionChange,
.readRemoteSupportedFeaturesComplete = BtmOnReadRemoteSupportedFeaturesComplete,
.readRemoteVersionInformationComplete = BtmOnReadRemoteVersionInformationComplete,
.commandStatus = BtmAclOnCommandStatus,
.roleChange = BtmOnRoleChange,
.readRemoteExtendedFeaturesComplete = BtmOnReadRemoteExtendedFeaturesComplete,
.readRssiComplete = BtmOnReadRssiComplete,
.leConnectionComplete = BtmOnLeConnectionComplete,
.leReadRemoteFeaturesComplete = BtmOnLeReadRemoteFeaturesComplete,
.leEnhancedConnectionComplete = BtmOnLeEnhancedConnectionComplete,
};