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