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 "btm_pm.h"
17
18 #include "btstack.h"
19 #include "hci/hci.h"
20 #include "hci/hci_error.h"
21 #include "platform/include/allocator.h"
22 #include "platform/include/list.h"
23 #include "platform/include/mutex.h"
24
25 #include "btm.h"
26 #include "btm_acl.h"
27
28 #define FOREACH_CALLBACKS_START(x) \
29 MutexLock(g_pmCallbackListLock); \
30 ListNode *node_ = ListGetFirstNode(g_pmCallbackList); \
31 while (node_ != NULL) { \
32 x = ListGetNodeData(node_); \
33 if ((x) != NULL) {
34
35 #define FOREACH_CALLBACKS_END \
36 } \
37 node_ = ListGetNextNode(node_); \
38 } \
39 MutexUnlock(g_pmCallbackListLock);
40
41 #define STATUS_NONE 0
42 #define STATUS_INITIALIZED 1
43
44 #define IS_INITIALIZED() (g_status == STATUS_INITIALIZED)
45
46 typedef struct {
47 const BtmPmCallbacks *callbacks;
48 void *context;
49 } BtmPmCallbackBlock;
50
51 typedef struct {
52 uint16_t connectionHandle;
53 uint8_t currentMode;
54 } BtmConnectionMode;
55
56 static List *g_pmCallbackList = NULL;
57 static Mutex *g_pmCallbackListLock = NULL;
58 static HciEventCallbacks g_hciEventCallbacks;
59
60 static List *g_connectionModeList = NULL;
61 static Mutex *g_connectionModeListLock = NULL;
62
63 static uint8_t g_status = STATUS_NONE;
64
BtmPmAllocCallbackBlock(const BtmPmCallbacks * callbacks,void * context)65 static BtmPmCallbackBlock *BtmPmAllocCallbackBlock(const BtmPmCallbacks *callbacks, void *context)
66 {
67 BtmPmCallbackBlock *block = MEM_MALLOC.alloc(sizeof(BtmPmCallbackBlock));
68 if (block != NULL) {
69 block->callbacks = callbacks;
70 block->context = context;
71 }
72 return block;
73 }
74
BtmPmFreeCallbackBlock(void * block)75 static void BtmPmFreeCallbackBlock(void *block)
76 {
77 MEM_MALLOC.free(block);
78 }
79
BtmAllocConnectionMode(uint16_t connectionHandle,uint8_t currentMode)80 static BtmConnectionMode *BtmAllocConnectionMode(uint16_t connectionHandle, uint8_t currentMode)
81 {
82 BtmConnectionMode *connectionMode = MEM_MALLOC.alloc(sizeof(BtmConnectionMode));
83 if (connectionMode != NULL) {
84 connectionMode->connectionHandle = connectionHandle;
85 connectionMode->currentMode = currentMode;
86 }
87 return connectionMode;
88 }
89
BtmFreeConnectionMode(void * connectionMode)90 static void BtmFreeConnectionMode(void *connectionMode)
91 {
92 MEM_MALLOC.free(connectionMode);
93 }
94
BtmFindConnectionModeByConnectionHandle(uint16_t connectionHandle)95 static BtmConnectionMode *BtmFindConnectionModeByConnectionHandle(uint16_t connectionHandle)
96 {
97 BtmConnectionMode *connectionMode = NULL;
98 ListNode *node = ListGetFirstNode(g_connectionModeList);
99
100 while (node != NULL) {
101 connectionMode = ListGetNodeData(node);
102 if (connectionMode->connectionHandle == connectionHandle) {
103 break;
104 } else {
105 connectionMode = NULL;
106 }
107 node = ListGetNextNode(node);
108 }
109
110 return connectionMode;
111 }
112
BtmInitPm()113 void BtmInitPm()
114 {
115 g_pmCallbackList = ListCreate(BtmPmFreeCallbackBlock);
116 g_pmCallbackListLock = MutexCreate();
117
118 g_connectionModeList = ListCreate(BtmFreeConnectionMode);
119 g_connectionModeListLock = MutexCreate();
120
121 g_status = STATUS_INITIALIZED;
122 }
123
BtmClosePm()124 void BtmClosePm()
125 {
126 g_status = STATUS_NONE;
127
128 if (g_connectionModeListLock != NULL) {
129 MutexDelete(g_connectionModeListLock);
130 g_connectionModeListLock = NULL;
131 }
132 if (g_connectionModeList != NULL) {
133 ListDelete(g_connectionModeList);
134 g_connectionModeList = NULL;
135 }
136 if (g_pmCallbackListLock != NULL) {
137 MutexDelete(g_pmCallbackListLock);
138 g_pmCallbackListLock = NULL;
139 }
140 if (g_pmCallbackList != NULL) {
141 ListDelete(g_pmCallbackList);
142 g_pmCallbackList = NULL;
143 }
144 }
145
BtmStartPm()146 void BtmStartPm()
147 {
148 HCI_RegisterEventCallbacks(&g_hciEventCallbacks);
149 }
150
BtmStopPm()151 void BtmStopPm()
152 {
153 HCI_DeregisterEventCallbacks(&g_hciEventCallbacks);
154
155 MutexLock(g_connectionModeListLock);
156 ListClear(g_connectionModeList);
157 MutexUnlock(g_connectionModeListLock);
158
159 MutexLock(g_pmCallbackListLock);
160 ListClear(g_pmCallbackList);
161 MutexUnlock(g_pmCallbackListLock);
162 }
163
BtmGetConnectionModeByConnectionHandle(uint16_t connectionHandle)164 static uint8_t BtmGetConnectionModeByConnectionHandle(uint16_t connectionHandle)
165 {
166 uint8_t currentMode = BTM_PM_ACTIVE_MODE;
167
168 MutexLock(g_connectionModeListLock);
169
170 BtmConnectionMode *connectionMode = BtmFindConnectionModeByConnectionHandle(connectionHandle);
171 if (connectionMode != NULL) {
172 currentMode = connectionMode->currentMode;
173 }
174
175 MutexUnlock(g_connectionModeListLock);
176
177 return currentMode;
178 }
179
BTM_EnterSniffMode(const BtAddr * addr,const BtmSniffParam * param)180 int BTM_EnterSniffMode(const BtAddr *addr, const BtmSniffParam *param)
181 {
182 if (addr == NULL || param == NULL) {
183 return BT_BAD_PARAM;
184 }
185
186 if (!IS_INITIALIZED()) {
187 return BT_BAD_STATUS;
188 }
189
190 uint16_t connectionHandle = 0;
191 int result = BtmGetAclHandleByAddress(addr, &connectionHandle);
192 if (result != BT_NO_ERROR) {
193 return BT_BAD_PARAM;
194 }
195
196 uint8_t currentMode = BtmGetConnectionModeByConnectionHandle(connectionHandle);
197 if (currentMode != BTM_PM_ACTIVE_MODE) {
198 return BT_BAD_STATUS;
199 }
200
201 HciSniffModeParam sniffModeParam = {
202 .connectionHandle = connectionHandle,
203 .sniffMaxInterval = param->maxInterval,
204 .sniffMinInterval = param->minInterval,
205 .sniffAttempt = param->attempt,
206 .sniffTimeout = param->timeout,
207 };
208
209 return HCI_SniffMode(&sniffModeParam);
210 }
211
BTM_ExitSniffMode(const BtAddr * addr)212 int BTM_ExitSniffMode(const BtAddr *addr)
213 {
214 if (addr == NULL) {
215 return BT_BAD_PARAM;
216 }
217
218 if (!IS_INITIALIZED()) {
219 return BT_BAD_STATUS;
220 }
221
222 uint16_t connectionHandle = 0;
223 int result = BtmGetAclHandleByAddress(addr, &connectionHandle);
224 if (result != BT_NO_ERROR) {
225 return BT_BAD_PARAM;
226 }
227
228 uint8_t currentMode = BtmGetConnectionModeByConnectionHandle(connectionHandle);
229 if (currentMode != BTM_PM_SNIFF_MODE) {
230 return BT_BAD_STATUS;
231 }
232
233 HciExitSniffModeParam param = {
234 .connectionHandle = connectionHandle,
235 };
236
237 return HCI_ExitSniffMode(¶m);
238 }
239
BTM_SetSniffSubrating(const BtAddr * addr,const BtmSniffSubrating * subRating)240 int BTM_SetSniffSubrating(const BtAddr *addr, const BtmSniffSubrating *subRating)
241 {
242 if ((addr == NULL) || (subRating == NULL)) {
243 return BT_BAD_PARAM;
244 }
245
246 if (!IS_INITIALIZED()) {
247 return BT_BAD_STATUS;
248 }
249
250 uint16_t connectionHandle = 0;
251 int result = BtmGetAclHandleByAddress(addr, &connectionHandle);
252 if (result != BT_NO_ERROR) {
253 return BT_BAD_STATUS;
254 }
255
256 HciSniffSubratingParam param = {
257 .connectionHandle = connectionHandle,
258 .maximumLatency = subRating->maximumLatency,
259 .minimumRemoteTimeout = subRating->minimumRemoteTimeout,
260 .minimumLocalTimeout = subRating->minimumLocalTimeout,
261 };
262
263 return HCI_SniffSubrating(¶m);
264 }
265
BtmUpdateConnectionMode(uint16_t connectionHandle,uint8_t currentMode)266 static void BtmUpdateConnectionMode(uint16_t connectionHandle, uint8_t currentMode)
267 {
268 MutexLock(g_connectionModeListLock);
269
270 BtmConnectionMode *connectionMode = BtmFindConnectionModeByConnectionHandle(connectionHandle);
271 if (connectionMode != NULL) {
272 connectionMode->currentMode = currentMode;
273 } else {
274 connectionMode = BtmAllocConnectionMode(connectionHandle, currentMode);
275 ListAddLast(g_connectionModeList, connectionMode);
276 }
277
278 MutexUnlock(g_connectionModeListLock);
279 }
280
BtmPmOnModeChange(const HciModeChangeEventParam * param)281 static void BtmPmOnModeChange(const HciModeChangeEventParam *param)
282 {
283 BtAddr addr;
284 int result = BtmGetAclAddressByHandle(param->connectionHandle, &addr);
285 if (result != BT_NO_ERROR) {
286 return;
287 }
288
289 if (param->status == HCI_SUCCESS) {
290 BtmUpdateConnectionMode(param->connectionHandle, param->currentMode);
291 }
292
293 BtmPmCallbackBlock *block = NULL;
294 FOREACH_CALLBACKS_START(block);
295 if (block->callbacks->modeChange != NULL) {
296 block->callbacks->modeChange(param->status, &addr, param->currentMode, param->interval, block->context);
297 }
298 FOREACH_CALLBACKS_END;
299 }
300
BtmPmOnSniffSubratingComplete(const HciSniffSubratingReturnParam * param)301 static void BtmPmOnSniffSubratingComplete(const HciSniffSubratingReturnParam *param)
302 {
303 BtAddr addr;
304 int result = BtmGetAclAddressByHandle(param->connectionHandle, &addr);
305 if (result != BT_NO_ERROR) {
306 return;
307 }
308
309 BtmPmCallbackBlock *block = NULL;
310 FOREACH_CALLBACKS_START(block);
311 if (block->callbacks->setSniffSubratingComplete != NULL) {
312 block->callbacks->setSniffSubratingComplete(param->status, &addr, block->context);
313 }
314 FOREACH_CALLBACKS_END;
315 }
316
BtmPmOnDisconnectComplete(const HciDisconnectCompleteEventParam * eventParam)317 void BtmPmOnDisconnectComplete(const HciDisconnectCompleteEventParam *eventParam)
318 {
319 if (eventParam->status != HCI_SUCCESS) {
320 return;
321 }
322
323 MutexLock(g_connectionModeListLock);
324
325 BtmConnectionMode *connectionMode = BtmFindConnectionModeByConnectionHandle(eventParam->connectionHandle);
326 if (connectionMode != NULL) {
327 ListRemoveNode(g_connectionModeList, connectionMode);
328 }
329
330 MutexUnlock(g_connectionModeListLock);
331 }
332
BTM_RegisterPmCallbacks(const BtmPmCallbacks * callbacks,void * context)333 int BTM_RegisterPmCallbacks(const BtmPmCallbacks *callbacks, void *context)
334 {
335 if (callbacks == NULL) {
336 return BT_BAD_PARAM;
337 }
338
339 if (!IS_INITIALIZED()) {
340 return BT_BAD_STATUS;
341 }
342
343 BtmPmCallbackBlock *block = BtmPmAllocCallbackBlock(callbacks, context);
344 if (block == NULL) {
345 return BT_NO_MEMORY;
346 }
347
348 MutexLock(g_pmCallbackListLock);
349 ListAddLast(g_pmCallbackList, block);
350 MutexUnlock(g_pmCallbackListLock);
351
352 return BT_NO_ERROR;
353 }
354
BTM_DeregisterPmCallbacks(const BtmPmCallbacks * callbacks)355 int BTM_DeregisterPmCallbacks(const BtmPmCallbacks *callbacks)
356 {
357 if (callbacks == NULL) {
358 return BT_BAD_PARAM;
359 }
360
361 if (!IS_INITIALIZED()) {
362 return BT_BAD_STATUS;
363 }
364
365 BtmPmCallbackBlock *block = NULL;
366 FOREACH_CALLBACKS_START(block);
367 if (block->callbacks == callbacks) {
368 ListRemoveNode(g_pmCallbackList, block);
369 break;
370 }
371 FOREACH_CALLBACKS_END;
372 return BT_NO_ERROR;
373 }
374
375 static HciEventCallbacks g_hciEventCallbacks = {
376 .disconnectComplete = BtmPmOnDisconnectComplete,
377 .modeChange = BtmPmOnModeChange,
378 .sniffSubratingComplete = BtmPmOnSniffSubratingComplete,
379 };