/* * Copyright (C) 2021 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 "hci_cmd.h" #include #include "btm/btm_thread.h" #include "btstack.h" #include "platform/include/alarm.h" #include "platform/include/allocator.h" #include "platform/include/bt_endian.h" #include "platform/include/list.h" #include "platform/include/mutex.h" #include "platform/include/queue.h" #include "hci/acl/hci_acl.h" #include "hci/hci.h" #include "hci/hci_def.h" #include "hci/hci_error.h" #include "hci/hci_failure.h" #include "hci/hci_internal.h" #include "hci_cmd_failure.h" #define MAX_QUEUE_SIZE 32 #define CMD_TIMEOUT (10 * 1000) #pragma pack(1) typedef struct { uint16_t opCode; uint8_t parameterTotalLength; } HciCmdHeader; #pragma pack() static uint8_t g_numberOfHciCmd = 1; static Mutex *g_lockNumberOfHciCmd = NULL; static Queue *g_cmdCache = NULL; static List *g_processingCmds = NULL; static Mutex *g_lockProcessingCmds = NULL; // Function declare static void HciFreeCmd(void *cmd); void HciInitCmd() { g_cmdCache = QueueCreate(MAX_QUEUE_SIZE); g_processingCmds = ListCreate(HciFreeCmd); g_numberOfHciCmd = 1; g_lockNumberOfHciCmd = MutexCreate(); g_lockProcessingCmds = MutexCreate(); } void HciCloseCmd() { if (g_lockProcessingCmds != NULL) { MutexDelete(g_lockProcessingCmds); g_lockProcessingCmds = NULL; } if (g_lockNumberOfHciCmd != NULL) { MutexDelete(g_lockNumberOfHciCmd); g_lockNumberOfHciCmd = NULL; } if (g_cmdCache != NULL) { QueueDelete(g_cmdCache, HciFreeCmd); g_cmdCache = NULL; } if (g_processingCmds != NULL) { ListDelete(g_processingCmds); g_processingCmds = NULL; } } static int HciCmdPushToTxQueue(HciCmd *cmd) { int result = BT_SUCCESS; HciPacket *hciPacket = MEM_MALLOC.alloc(sizeof(HciPacket)); if (hciPacket != NULL) { hciPacket->type = H2C_CMD; hciPacket->packet = cmd->packet; cmd->packet = NULL; HciPushToTxQueue(hciPacket); } else { result = BT_NO_MEMORY; } return result; } static void HciCmdTimeoutTask(void *context) { HciCmd *pCmd = NULL; uint16_t opCode = 0; MutexLock(g_lockProcessingCmds); ListNode *node = ListGetFirstNode(g_processingCmds); while (node != NULL) { pCmd = ListGetNodeData(node); if (pCmd == context) { opCode = pCmd->opCode; break; } else { pCmd = NULL; } node = ListGetNextNode(node); } MutexUnlock(g_lockProcessingCmds); if (opCode == 0) { return; } HciCmdOnCommandStatus(opCode, HCI_TIMEOUT); HciOnCmdTimeout(); } static void HciCmdOnCmdTimeout(void *parameter) { Thread *thread = BTM_GetProcessingThread(); if (thread != NULL) { ThreadPostTask(thread, HciCmdTimeoutTask, parameter); } } void HciSetNumberOfHciCmd(uint8_t numberOfHciCmd) { MutexLock(g_lockNumberOfHciCmd); g_numberOfHciCmd = numberOfHciCmd; HciCmd *cmd = NULL; while (g_numberOfHciCmd > 0) { cmd = QueueTryDequeue(g_cmdCache); if (cmd != NULL) { int result = HciCmdPushToTxQueue(cmd); if (result == BT_SUCCESS) { g_numberOfHciCmd--; MutexLock(g_lockProcessingCmds); AlarmSet(cmd->alarm, CMD_TIMEOUT, HciCmdOnCmdTimeout, cmd); ListAddLast(g_processingCmds, cmd); MutexUnlock(g_lockProcessingCmds); } else { HciFreeCmd(cmd); } } else { // No more cmd break; } } MutexUnlock(g_lockNumberOfHciCmd); } static Packet *HciCreateCmdPacket(uint16_t opCode) { Packet *packet = PacketMalloc(sizeof(HciCmdHeader), 0, 0); Buffer *headerBuffer = PacketHead(packet); HciCmdHeader *header = (HciCmdHeader *)BufferPtr(headerBuffer); if (header != NULL) { header->opCode = opCode; header->parameterTotalLength = 0; } return packet; } static Packet *HciCreateCmdPacketWithParam(uint16_t opCode, const void *param, uint8_t paramLength) { Packet *packet = PacketMalloc(sizeof(HciCmdHeader), 0, paramLength); Buffer *headerBuffer = PacketHead(packet); HciCmdHeader *header = (HciCmdHeader *)BufferPtr(headerBuffer); if (header != NULL) { header->opCode = opCode; header->parameterTotalLength = paramLength; } PacketPayloadWrite(packet, param, 0, paramLength); return packet; } HciCmd *HciAllocCmd(uint16_t opCode, const void *param, size_t paramLength) { HciCmd *cmd = MEM_MALLOC.alloc(sizeof(HciCmd)); if (cmd != NULL) { cmd->opCode = opCode; if (param != NULL && paramLength > 0) { cmd->param = MEM_MALLOC.alloc(paramLength); if (cmd->param != NULL) { (void)memcpy_s(cmd->param, paramLength, param, paramLength); } cmd->packet = HciCreateCmdPacketWithParam(opCode, param, paramLength); } else { cmd->packet = HciCreateCmdPacket(opCode); cmd->param = NULL; } cmd->alarm = AlarmCreate(NULL, false); } return cmd; } static void HciFreeCmd(void *cmd) { HciCmd *hciCmd = (HciCmd *)cmd; if (hciCmd != NULL) { if (hciCmd->alarm != NULL) { AlarmCancel(hciCmd->alarm); AlarmDelete(hciCmd->alarm); } if (hciCmd->param != NULL) { MEM_MALLOC.free(hciCmd->param); hciCmd->param = NULL; } if (hciCmd->packet != NULL) { PacketFree(hciCmd->packet); } } MEM_MALLOC.free(cmd); } int HciSendCmd(HciCmd *cmd) { int result = BT_SUCCESS; MutexLock(g_lockNumberOfHciCmd); if (g_numberOfHciCmd > 0) { result = HciCmdPushToTxQueue(cmd); if (result == BT_SUCCESS) { g_numberOfHciCmd--; MutexLock(g_lockProcessingCmds); AlarmSet(cmd->alarm, CMD_TIMEOUT, HciCmdOnCmdTimeout, cmd); ListAddLast(g_processingCmds, cmd); MutexUnlock(g_lockProcessingCmds); } } else { QueueEnqueue(g_cmdCache, cmd); } MutexUnlock(g_lockNumberOfHciCmd); return result; } void HciCmdOnCommandStatus(uint16_t opCode, uint8_t status) { HciCmd *cmd = NULL; MutexLock(g_lockProcessingCmds); ListNode *node = ListGetFirstNode(g_processingCmds); while (node != NULL) { cmd = ListGetNodeData(node); if (cmd != NULL) { if (opCode == cmd->opCode) { break; } cmd = NULL; } node = ListGetNextNode(node); } void *param = NULL; if (cmd != NULL) { AlarmCancel(cmd->alarm); param = cmd->param; cmd->param = NULL; ListRemoveNode(g_processingCmds, cmd); } MutexUnlock(g_lockProcessingCmds); if (cmd != NULL && opCode == HCI_DISCONNECT && status == HCI_SUCCESS) { HciDisconnectParam *discParam = (HciDisconnectParam *)param; HciAclOnDisconnectStatus(discParam->connectionHandle); } if (cmd != NULL && status != HCI_SUCCESS) { HciOnCmdFailed(opCode, status, param); } if (param != NULL) { MEM_MALLOC.free(param); param = NULL; } } void HciCmdOnCommandComplete(uint16_t opCode) { HciCmd *cmd = NULL; MutexLock(g_lockProcessingCmds); ListNode *node = ListGetFirstNode(g_processingCmds); while (node != NULL) { cmd = ListGetNodeData(node); if (cmd != NULL) { if (opCode == cmd->opCode) { break; } cmd = NULL; } node = ListGetNextNode(node); } if (cmd != NULL) { AlarmCancel(cmd->alarm); ListRemoveNode(g_processingCmds, cmd); } MutexUnlock(g_lockProcessingCmds); }