1 /*
2  * Copyright (C) 2021 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://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,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "hci_cmd.h"
17 
18 #include <securec.h>
19 
20 #include "btm/btm_thread.h"
21 #include "btstack.h"
22 #include "platform/include/alarm.h"
23 #include "platform/include/allocator.h"
24 #include "platform/include/bt_endian.h"
25 #include "platform/include/list.h"
26 #include "platform/include/mutex.h"
27 #include "platform/include/queue.h"
28 
29 #include "hci/acl/hci_acl.h"
30 #include "hci/hci.h"
31 #include "hci/hci_def.h"
32 #include "hci/hci_error.h"
33 #include "hci/hci_failure.h"
34 #include "hci/hci_internal.h"
35 
36 #include "hci_cmd_failure.h"
37 
38 #define MAX_QUEUE_SIZE 32
39 
40 #define CMD_TIMEOUT (10 * 1000)
41 
42 #pragma pack(1)
43 typedef struct {
44     uint16_t opCode;
45     uint8_t parameterTotalLength;
46 } HciCmdHeader;
47 #pragma pack()
48 
49 static uint8_t g_numberOfHciCmd = 1;
50 static Mutex *g_lockNumberOfHciCmd = NULL;
51 
52 static Queue *g_cmdCache = NULL;
53 
54 static List *g_processingCmds = NULL;
55 static Mutex *g_lockProcessingCmds = NULL;
56 
57 // Function declare
58 static void HciFreeCmd(void *cmd);
59 
HciInitCmd()60 void HciInitCmd()
61 {
62     g_cmdCache = QueueCreate(MAX_QUEUE_SIZE);
63     g_processingCmds = ListCreate(HciFreeCmd);
64 
65     g_numberOfHciCmd = 1;
66     g_lockNumberOfHciCmd = MutexCreate();
67     g_lockProcessingCmds = MutexCreate();
68 }
69 
HciCloseCmd()70 void HciCloseCmd()
71 {
72     if (g_lockProcessingCmds != NULL) {
73         MutexDelete(g_lockProcessingCmds);
74         g_lockProcessingCmds = NULL;
75     }
76 
77     if (g_lockNumberOfHciCmd != NULL) {
78         MutexDelete(g_lockNumberOfHciCmd);
79         g_lockNumberOfHciCmd = NULL;
80     }
81 
82     if (g_cmdCache != NULL) {
83         QueueDelete(g_cmdCache, HciFreeCmd);
84         g_cmdCache = NULL;
85     }
86 
87     if (g_processingCmds != NULL) {
88         ListDelete(g_processingCmds);
89         g_processingCmds = NULL;
90     }
91 }
92 
HciCmdPushToTxQueue(HciCmd * cmd)93 static int HciCmdPushToTxQueue(HciCmd *cmd)
94 {
95     int result = BT_SUCCESS;
96     HciPacket *hciPacket = MEM_MALLOC.alloc(sizeof(HciPacket));
97     if (hciPacket != NULL) {
98         hciPacket->type = H2C_CMD;
99         hciPacket->packet = cmd->packet;
100         cmd->packet = NULL;
101         HciPushToTxQueue(hciPacket);
102     } else {
103         result = BT_NO_MEMORY;
104     }
105     return result;
106 }
107 
HciCmdTimeoutTask(void * context)108 static void HciCmdTimeoutTask(void *context)
109 {
110     HciCmd *pCmd = NULL;
111     uint16_t opCode = 0;
112 
113     MutexLock(g_lockProcessingCmds);
114     ListNode *node = ListGetFirstNode(g_processingCmds);
115     while (node != NULL) {
116         pCmd = ListGetNodeData(node);
117         if (pCmd == context) {
118             opCode = pCmd->opCode;
119             break;
120         } else {
121             pCmd = NULL;
122         }
123         node = ListGetNextNode(node);
124     }
125     MutexUnlock(g_lockProcessingCmds);
126 
127     if (opCode == 0) {
128         return;
129     }
130 
131     HciCmdOnCommandStatus(opCode, HCI_TIMEOUT);
132 
133     HciOnCmdTimeout();
134 }
135 
HciCmdOnCmdTimeout(void * parameter)136 static void HciCmdOnCmdTimeout(void *parameter)
137 {
138     Thread *thread = BTM_GetProcessingThread();
139     if (thread != NULL) {
140         ThreadPostTask(thread, HciCmdTimeoutTask, parameter);
141     }
142 }
143 
HciSetNumberOfHciCmd(uint8_t numberOfHciCmd)144 void HciSetNumberOfHciCmd(uint8_t numberOfHciCmd)
145 {
146     MutexLock(g_lockNumberOfHciCmd);
147 
148     g_numberOfHciCmd = numberOfHciCmd;
149 
150     HciCmd *cmd = NULL;
151 
152     while (g_numberOfHciCmd > 0) {
153         cmd = QueueTryDequeue(g_cmdCache);
154         if (cmd != NULL) {
155             int result = HciCmdPushToTxQueue(cmd);
156             if (result == BT_SUCCESS) {
157                 g_numberOfHciCmd--;
158 
159                 MutexLock(g_lockProcessingCmds);
160                 AlarmSet(cmd->alarm, CMD_TIMEOUT, HciCmdOnCmdTimeout, cmd);
161                 ListAddLast(g_processingCmds, cmd);
162                 MutexUnlock(g_lockProcessingCmds);
163             } else {
164                 HciFreeCmd(cmd);
165             }
166         } else {
167             // No more cmd
168             break;
169         }
170     }
171 
172     MutexUnlock(g_lockNumberOfHciCmd);
173 }
174 
HciCreateCmdPacket(uint16_t opCode)175 static Packet *HciCreateCmdPacket(uint16_t opCode)
176 {
177     Packet *packet = PacketMalloc(sizeof(HciCmdHeader), 0, 0);
178     Buffer *headerBuffer = PacketHead(packet);
179     HciCmdHeader *header = (HciCmdHeader *)BufferPtr(headerBuffer);
180     if (header != NULL) {
181         header->opCode = opCode;
182         header->parameterTotalLength = 0;
183     }
184 
185     return packet;
186 }
187 
HciCreateCmdPacketWithParam(uint16_t opCode,const void * param,uint8_t paramLength)188 static Packet *HciCreateCmdPacketWithParam(uint16_t opCode, const void *param, uint8_t paramLength)
189 {
190     Packet *packet = PacketMalloc(sizeof(HciCmdHeader), 0, paramLength);
191     Buffer *headerBuffer = PacketHead(packet);
192     HciCmdHeader *header = (HciCmdHeader *)BufferPtr(headerBuffer);
193     if (header != NULL) {
194         header->opCode = opCode;
195         header->parameterTotalLength = paramLength;
196     }
197     PacketPayloadWrite(packet, param, 0, paramLength);
198     return packet;
199 }
200 
HciAllocCmd(uint16_t opCode,const void * param,size_t paramLength)201 HciCmd *HciAllocCmd(uint16_t opCode, const void *param, size_t paramLength)
202 {
203     HciCmd *cmd = MEM_MALLOC.alloc(sizeof(HciCmd));
204     if (cmd != NULL) {
205         cmd->opCode = opCode;
206         if (param != NULL && paramLength > 0) {
207             cmd->param = MEM_MALLOC.alloc(paramLength);
208             if (cmd->param != NULL) {
209                 (void)memcpy_s(cmd->param, paramLength, param, paramLength);
210             }
211             cmd->packet = HciCreateCmdPacketWithParam(opCode, param, paramLength);
212         } else {
213             cmd->packet = HciCreateCmdPacket(opCode);
214             cmd->param = NULL;
215         }
216         cmd->alarm = AlarmCreate(NULL, false);
217     }
218     return cmd;
219 }
220 
HciFreeCmd(void * cmd)221 static void HciFreeCmd(void *cmd)
222 {
223     HciCmd *hciCmd = (HciCmd *)cmd;
224     if (hciCmd != NULL) {
225         if (hciCmd->alarm != NULL) {
226             AlarmCancel(hciCmd->alarm);
227             AlarmDelete(hciCmd->alarm);
228         }
229         if (hciCmd->param != NULL) {
230             MEM_MALLOC.free(hciCmd->param);
231             hciCmd->param = NULL;
232         }
233         if (hciCmd->packet != NULL) {
234             PacketFree(hciCmd->packet);
235         }
236     }
237     MEM_MALLOC.free(cmd);
238 }
239 
HciSendCmd(HciCmd * cmd)240 int HciSendCmd(HciCmd *cmd)
241 {
242     int result = BT_SUCCESS;
243 
244     MutexLock(g_lockNumberOfHciCmd);
245 
246     if (g_numberOfHciCmd > 0) {
247         result = HciCmdPushToTxQueue(cmd);
248         if (result == BT_SUCCESS) {
249             g_numberOfHciCmd--;
250 
251             MutexLock(g_lockProcessingCmds);
252             AlarmSet(cmd->alarm, CMD_TIMEOUT, HciCmdOnCmdTimeout, cmd);
253             ListAddLast(g_processingCmds, cmd);
254             MutexUnlock(g_lockProcessingCmds);
255         }
256     } else {
257         QueueEnqueue(g_cmdCache, cmd);
258     }
259 
260     MutexUnlock(g_lockNumberOfHciCmd);
261 
262     return result;
263 }
264 
HciCmdOnCommandStatus(uint16_t opCode,uint8_t status)265 void HciCmdOnCommandStatus(uint16_t opCode, uint8_t status)
266 {
267     HciCmd *cmd = NULL;
268 
269     MutexLock(g_lockProcessingCmds);
270 
271     ListNode *node = ListGetFirstNode(g_processingCmds);
272     while (node != NULL) {
273         cmd = ListGetNodeData(node);
274         if (cmd != NULL) {
275             if (opCode == cmd->opCode) {
276                 break;
277             }
278             cmd = NULL;
279         }
280 
281         node = ListGetNextNode(node);
282     }
283 
284     void *param = NULL;
285 
286     if (cmd != NULL) {
287         AlarmCancel(cmd->alarm);
288 
289         param = cmd->param;
290         cmd->param = NULL;
291 
292         ListRemoveNode(g_processingCmds, cmd);
293     }
294 
295     MutexUnlock(g_lockProcessingCmds);
296 
297     if (cmd != NULL && opCode == HCI_DISCONNECT && status == HCI_SUCCESS) {
298         HciDisconnectParam *discParam = (HciDisconnectParam *)param;
299         HciAclOnDisconnectStatus(discParam->connectionHandle);
300     }
301 
302     if (cmd != NULL && status != HCI_SUCCESS) {
303         HciOnCmdFailed(opCode, status, param);
304     }
305 
306     if (param != NULL) {
307         MEM_MALLOC.free(param);
308         param = NULL;
309     }
310 }
311 
HciCmdOnCommandComplete(uint16_t opCode)312 void HciCmdOnCommandComplete(uint16_t opCode)
313 {
314     HciCmd *cmd = NULL;
315 
316     MutexLock(g_lockProcessingCmds);
317 
318     ListNode *node = ListGetFirstNode(g_processingCmds);
319     while (node != NULL) {
320         cmd = ListGetNodeData(node);
321         if (cmd != NULL) {
322             if (opCode == cmd->opCode) {
323                 break;
324             }
325             cmd = NULL;
326         }
327 
328         node = ListGetNextNode(node);
329     }
330 
331     if (cmd != NULL) {
332         AlarmCancel(cmd->alarm);
333         ListRemoveNode(g_processingCmds, cmd);
334     }
335 
336     MutexUnlock(g_lockProcessingCmds);
337 }
338