• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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(&param);
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(&param);
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 };