• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /******************************************************************************
2  *
3  *  Copyright 2008-2012 Broadcom Corporation
4  *
5  *  Licensed under the Apache License, Version 2.0 (the "License");
6  *  you may not use this file except in compliance with the License.
7  *  You may obtain a copy of the License at:
8  *
9  *  http://www.apache.org/licenses/LICENSE-2.0
10  *
11  *  Unless required by applicable law or agreed to in writing, software
12  *  distributed under the License is distributed on an "AS IS" BASIS,
13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *  See the License for the specific language governing permissions and
15  *  limitations under the License.
16  *
17  ******************************************************************************/
18 
19 /******************************************************************************
20  *
21  *  this file contains the main GATT server attributes access request
22  *  handling functions.
23  *
24  ******************************************************************************/
25 
26 #include "bt_target.h"
27 #include "bt_utils.h"
28 
29 #include "gatt_api.h"
30 #include "gatt_int.h"
31 #include "osi/include/osi.h"
32 
33 using base::StringPrintf;
34 using bluetooth::Uuid;
35 
36 #define GATTP_MAX_NUM_INC_SVR 0
37 #define GATTP_MAX_CHAR_NUM 2
38 #define GATTP_MAX_ATTR_NUM (GATTP_MAX_CHAR_NUM * 2 + GATTP_MAX_NUM_INC_SVR + 1)
39 #define GATTP_MAX_CHAR_VALUE_SIZE 50
40 
41 #ifndef GATTP_ATTR_DB_SIZE
42 #define GATTP_ATTR_DB_SIZE                                    \
43   GATT_DB_MEM_SIZE(GATTP_MAX_NUM_INC_SVR, GATTP_MAX_CHAR_NUM, \
44                    GATTP_MAX_CHAR_VALUE_SIZE)
45 #endif
46 
47 static void gatt_request_cback(uint16_t conn_id, uint32_t trans_id,
48                                uint8_t op_code, tGATTS_DATA* p_data);
49 static void gatt_connect_cback(UNUSED_ATTR tGATT_IF gatt_if,
50                                const RawAddress& bda, uint16_t conn_id,
51                                bool connected, tGATT_DISCONN_REASON reason,
52                                tBT_TRANSPORT transport);
53 static void gatt_disc_res_cback(uint16_t conn_id, tGATT_DISC_TYPE disc_type,
54                                 tGATT_DISC_RES* p_data);
55 static void gatt_disc_cmpl_cback(uint16_t conn_id, tGATT_DISC_TYPE disc_type,
56                                  tGATT_STATUS status);
57 static void gatt_cl_op_cmpl_cback(UNUSED_ATTR uint16_t conn_id,
58                                   UNUSED_ATTR tGATTC_OPTYPE op,
59                                   UNUSED_ATTR tGATT_STATUS status,
60                                   UNUSED_ATTR tGATT_CL_COMPLETE* p_data);
61 
62 static void gatt_cl_start_config_ccc(tGATT_PROFILE_CLCB* p_clcb);
63 
64 static tGATT_CBACK gatt_profile_cback = {gatt_connect_cback,
65                                          gatt_cl_op_cmpl_cback,
66                                          gatt_disc_res_cback,
67                                          gatt_disc_cmpl_cback,
68                                          gatt_request_cback,
69                                          NULL,
70                                          NULL,
71                                          NULL,
72                                          NULL};
73 
74 /*******************************************************************************
75  *
76  * Function         gatt_profile_find_conn_id_by_bd_addr
77  *
78  * Description      Find the connection ID by remote address
79  *
80  * Returns          Connection ID
81  *
82  ******************************************************************************/
gatt_profile_find_conn_id_by_bd_addr(const RawAddress & remote_bda)83 uint16_t gatt_profile_find_conn_id_by_bd_addr(const RawAddress& remote_bda) {
84   uint16_t conn_id = GATT_INVALID_CONN_ID;
85   GATT_GetConnIdIfConnected(gatt_cb.gatt_if, remote_bda, &conn_id,
86                             BT_TRANSPORT_LE);
87   if (conn_id == GATT_INVALID_CONN_ID)
88     GATT_GetConnIdIfConnected(gatt_cb.gatt_if, remote_bda, &conn_id,
89                               BT_TRANSPORT_BR_EDR);
90   return conn_id;
91 }
92 
93 /*******************************************************************************
94  *
95  * Function         gatt_profile_find_clcb_by_conn_id
96  *
97  * Description      find clcb by Connection ID
98  *
99  * Returns          Pointer to the found link conenction control block.
100  *
101  ******************************************************************************/
gatt_profile_find_clcb_by_conn_id(uint16_t conn_id)102 static tGATT_PROFILE_CLCB* gatt_profile_find_clcb_by_conn_id(uint16_t conn_id) {
103   uint8_t i_clcb;
104   tGATT_PROFILE_CLCB* p_clcb = NULL;
105 
106   for (i_clcb = 0, p_clcb = gatt_cb.profile_clcb; i_clcb < GATT_MAX_APPS;
107        i_clcb++, p_clcb++) {
108     if (p_clcb->in_use && p_clcb->conn_id == conn_id) return p_clcb;
109   }
110 
111   return NULL;
112 }
113 
114 /*******************************************************************************
115  *
116  * Function         gatt_profile_find_clcb_by_bd_addr
117  *
118  * Description      The function searches all LCBs with macthing bd address.
119  *
120  * Returns          Pointer to the found link conenction control block.
121  *
122  ******************************************************************************/
gatt_profile_find_clcb_by_bd_addr(const RawAddress & bda,tBT_TRANSPORT transport)123 static tGATT_PROFILE_CLCB* gatt_profile_find_clcb_by_bd_addr(
124     const RawAddress& bda, tBT_TRANSPORT transport) {
125   uint8_t i_clcb;
126   tGATT_PROFILE_CLCB* p_clcb = NULL;
127 
128   for (i_clcb = 0, p_clcb = gatt_cb.profile_clcb; i_clcb < GATT_MAX_APPS;
129        i_clcb++, p_clcb++) {
130     if (p_clcb->in_use && p_clcb->transport == transport && p_clcb->connected &&
131         p_clcb->bda == bda)
132       return p_clcb;
133   }
134 
135   return NULL;
136 }
137 
138 /*******************************************************************************
139  *
140  * Function         gatt_profile_clcb_alloc
141  *
142  * Description      The function allocates a GATT profile connection link
143  *                  control block
144  *
145  * Returns          NULL if not found. Otherwise pointer to the connection link
146  *                  block.
147  *
148  ******************************************************************************/
gatt_profile_clcb_alloc(uint16_t conn_id,const RawAddress & bda,tBT_TRANSPORT tranport)149 tGATT_PROFILE_CLCB* gatt_profile_clcb_alloc(uint16_t conn_id,
150                                             const RawAddress& bda,
151                                             tBT_TRANSPORT tranport) {
152   uint8_t i_clcb = 0;
153   tGATT_PROFILE_CLCB* p_clcb = NULL;
154 
155   for (i_clcb = 0, p_clcb = gatt_cb.profile_clcb; i_clcb < GATT_MAX_APPS;
156        i_clcb++, p_clcb++) {
157     if (!p_clcb->in_use) {
158       p_clcb->in_use = true;
159       p_clcb->conn_id = conn_id;
160       p_clcb->connected = true;
161       p_clcb->transport = tranport;
162       p_clcb->bda = bda;
163       break;
164     }
165   }
166   if (i_clcb < GATT_MAX_APPS) return p_clcb;
167 
168   return NULL;
169 }
170 
171 /*******************************************************************************
172  *
173  * Function         gatt_profile_clcb_dealloc
174  *
175  * Description      The function deallocates a GATT profile connection link
176  *                  control block
177  *
178  * Returns          void
179  *
180  ******************************************************************************/
gatt_profile_clcb_dealloc(tGATT_PROFILE_CLCB * p_clcb)181 void gatt_profile_clcb_dealloc(tGATT_PROFILE_CLCB* p_clcb) {
182   memset(p_clcb, 0, sizeof(tGATT_PROFILE_CLCB));
183 }
184 
185 /*******************************************************************************
186  *
187  * Function         gatt_request_cback
188  *
189  * Description      GATT profile attribute access request callback.
190  *
191  * Returns          void.
192  *
193  ******************************************************************************/
gatt_request_cback(uint16_t conn_id,uint32_t trans_id,tGATTS_REQ_TYPE type,tGATTS_DATA * p_data)194 static void gatt_request_cback(uint16_t conn_id, uint32_t trans_id,
195                                tGATTS_REQ_TYPE type, tGATTS_DATA* p_data) {
196   uint8_t status = GATT_INVALID_PDU;
197   tGATTS_RSP rsp_msg;
198   bool ignore = false;
199 
200   memset(&rsp_msg, 0, sizeof(tGATTS_RSP));
201 
202   switch (type) {
203     case GATTS_REQ_TYPE_READ_CHARACTERISTIC:
204     case GATTS_REQ_TYPE_READ_DESCRIPTOR:
205       status = GATT_READ_NOT_PERMIT;
206       break;
207 
208     case GATTS_REQ_TYPE_WRITE_CHARACTERISTIC:
209     case GATTS_REQ_TYPE_WRITE_DESCRIPTOR:
210       status = GATT_WRITE_NOT_PERMIT;
211       break;
212 
213     case GATTS_REQ_TYPE_WRITE_EXEC:
214     case GATT_CMD_WRITE:
215       ignore = true;
216       VLOG(1) << "Ignore GATT_REQ_EXEC_WRITE/WRITE_CMD";
217       break;
218 
219     case GATTS_REQ_TYPE_MTU:
220       VLOG(1) << "Get MTU exchange new mtu size: " << +p_data->mtu;
221       ignore = true;
222       break;
223 
224     default:
225       VLOG(1) << "Unknown/unexpected LE GAP ATT request: " << loghex(type);
226       break;
227   }
228 
229   if (!ignore) GATTS_SendRsp(conn_id, trans_id, status, &rsp_msg);
230 }
231 
232 /*******************************************************************************
233  *
234  * Function         gatt_connect_cback
235  *
236  * Description      Gatt profile connection callback.
237  *
238  * Returns          void
239  *
240  ******************************************************************************/
gatt_connect_cback(UNUSED_ATTR tGATT_IF gatt_if,const RawAddress & bda,uint16_t conn_id,bool connected,tGATT_DISCONN_REASON reason,tBT_TRANSPORT transport)241 static void gatt_connect_cback(UNUSED_ATTR tGATT_IF gatt_if,
242                                const RawAddress& bda, uint16_t conn_id,
243                                bool connected, tGATT_DISCONN_REASON reason,
244                                tBT_TRANSPORT transport) {
245   VLOG(1) << __func__ << ": from " << bda << " connected: " << connected
246           << ", conn_id: " << loghex(conn_id) << "reason: " << loghex(reason);
247 
248   tGATT_PROFILE_CLCB* p_clcb =
249       gatt_profile_find_clcb_by_bd_addr(bda, transport);
250   if (p_clcb == NULL) return;
251 
252   if (connected) {
253     p_clcb->conn_id = conn_id;
254     p_clcb->connected = true;
255 
256     if (p_clcb->ccc_stage == GATT_SVC_CHANGED_CONNECTING) {
257       p_clcb->ccc_stage++;
258       gatt_cl_start_config_ccc(p_clcb);
259     }
260   } else {
261     gatt_profile_clcb_dealloc(p_clcb);
262   }
263 }
264 
265 /*******************************************************************************
266  *
267  * Function         gatt_profile_db_init
268  *
269  * Description      Initializa the GATT profile attribute database.
270  *
271  ******************************************************************************/
gatt_profile_db_init(void)272 void gatt_profile_db_init(void) {
273   uint16_t service_handle = 0;
274 
275   /* Fill our internal UUID with a fixed pattern 0x81 */
276   std::array<uint8_t, Uuid::kNumBytes128> tmp;
277   tmp.fill(0x81);
278 
279   /* Create a GATT profile service */
280   gatt_cb.gatt_if = GATT_Register(Uuid::From128BitBE(tmp), &gatt_profile_cback);
281   GATT_StartIf(gatt_cb.gatt_if);
282 
283   Uuid service_uuid = Uuid::From16Bit(UUID_SERVCLASS_GATT_SERVER);
284 
285   Uuid char_uuid = Uuid::From16Bit(GATT_UUID_GATT_SRV_CHGD);
286 
287   btgatt_db_element_t service[] = {
288       {.type = BTGATT_DB_PRIMARY_SERVICE, .uuid = service_uuid},
289       {.type = BTGATT_DB_CHARACTERISTIC,
290        .uuid = char_uuid,
291        .properties = GATT_CHAR_PROP_BIT_INDICATE,
292        .permissions = 0}};
293 
294   GATTS_AddService(gatt_cb.gatt_if, service,
295                    sizeof(service) / sizeof(btgatt_db_element_t));
296 
297   service_handle = service[0].attribute_handle;
298   gatt_cb.handle_of_h_r = service[1].attribute_handle;
299 
300   VLOG(1) << __func__ << ": gatt_if=" << +gatt_cb.gatt_if;
301 }
302 
303 /*******************************************************************************
304  *
305  * Function         gatt_disc_res_cback
306  *
307  * Description      Gatt profile discovery result callback
308  *
309  * Returns          void
310  *
311  ******************************************************************************/
gatt_disc_res_cback(uint16_t conn_id,tGATT_DISC_TYPE disc_type,tGATT_DISC_RES * p_data)312 static void gatt_disc_res_cback(uint16_t conn_id, tGATT_DISC_TYPE disc_type,
313                                 tGATT_DISC_RES* p_data) {
314   tGATT_PROFILE_CLCB* p_clcb = gatt_profile_find_clcb_by_conn_id(conn_id);
315 
316   if (p_clcb == NULL) return;
317 
318   switch (disc_type) {
319     case GATT_DISC_SRVC_BY_UUID: /* stage 1 */
320       p_clcb->e_handle = p_data->value.group_value.e_handle;
321       p_clcb->ccc_result++;
322       break;
323 
324     case GATT_DISC_CHAR: /* stage 2 */
325       p_clcb->s_handle = p_data->value.dclr_value.val_handle;
326       p_clcb->ccc_result++;
327       break;
328 
329     case GATT_DISC_CHAR_DSCPT: /* stage 3 */
330       if (p_data->type == Uuid::From16Bit(GATT_UUID_CHAR_CLIENT_CONFIG)) {
331         p_clcb->s_handle = p_data->handle;
332         p_clcb->ccc_result++;
333       }
334       break;
335   }
336 }
337 
338 /*******************************************************************************
339  *
340  * Function         gatt_disc_cmpl_cback
341  *
342  * Description      Gatt profile discovery complete callback
343  *
344  * Returns          void
345  *
346  ******************************************************************************/
gatt_disc_cmpl_cback(uint16_t conn_id,tGATT_DISC_TYPE disc_type,tGATT_STATUS status)347 static void gatt_disc_cmpl_cback(uint16_t conn_id, tGATT_DISC_TYPE disc_type,
348                                  tGATT_STATUS status) {
349   tGATT_PROFILE_CLCB* p_clcb = gatt_profile_find_clcb_by_conn_id(conn_id);
350 
351   if (p_clcb == NULL) return;
352 
353   if (status != GATT_SUCCESS || p_clcb->ccc_result == 0) {
354     LOG(WARNING) << __func__
355                  << ": Unable to register for service changed indication";
356     return;
357   }
358 
359   p_clcb->ccc_result = 0;
360   p_clcb->ccc_stage++;
361   gatt_cl_start_config_ccc(p_clcb);
362 }
363 
364 /*******************************************************************************
365  *
366  * Function         gatt_cl_op_cmpl_cback
367  *
368  * Description      Gatt profile client operation complete callback
369  *
370  * Returns          void
371  *
372  ******************************************************************************/
gatt_cl_op_cmpl_cback(UNUSED_ATTR uint16_t conn_id,UNUSED_ATTR tGATTC_OPTYPE op,UNUSED_ATTR tGATT_STATUS status,UNUSED_ATTR tGATT_CL_COMPLETE * p_data)373 static void gatt_cl_op_cmpl_cback(UNUSED_ATTR uint16_t conn_id,
374                                   UNUSED_ATTR tGATTC_OPTYPE op,
375                                   UNUSED_ATTR tGATT_STATUS status,
376                                   UNUSED_ATTR tGATT_CL_COMPLETE* p_data) {}
377 
378 /*******************************************************************************
379  *
380  * Function         gatt_cl_start_config_ccc
381  *
382  * Description      Gatt profile start configure service change CCC
383  *
384  * Returns          void
385  *
386  ******************************************************************************/
gatt_cl_start_config_ccc(tGATT_PROFILE_CLCB * p_clcb)387 static void gatt_cl_start_config_ccc(tGATT_PROFILE_CLCB* p_clcb) {
388 
389   VLOG(1) << __func__ << ": stage: " << +p_clcb->ccc_stage;
390 
391   switch (p_clcb->ccc_stage) {
392     case GATT_SVC_CHANGED_SERVICE: /* discover GATT service */
393       GATTC_Discover(p_clcb->conn_id, GATT_DISC_SRVC_BY_UUID, 0x0001, 0xffff,
394                      Uuid::From16Bit(UUID_SERVCLASS_GATT_SERVER));
395       break;
396 
397     case GATT_SVC_CHANGED_CHARACTERISTIC: /* discover service change char */
398       GATTC_Discover(p_clcb->conn_id, GATT_DISC_CHAR, 0x0001, p_clcb->e_handle,
399                      Uuid::From16Bit(GATT_UUID_GATT_SRV_CHGD));
400       break;
401 
402     case GATT_SVC_CHANGED_DESCRIPTOR: /* discover service change ccc */
403       GATTC_Discover(p_clcb->conn_id, GATT_DISC_CHAR_DSCPT, p_clcb->s_handle,
404                      p_clcb->e_handle);
405       break;
406 
407     case GATT_SVC_CHANGED_CONFIGURE_CCCD: /* write ccc */
408     {
409       tGATT_VALUE ccc_value;
410       memset(&ccc_value, 0, sizeof(tGATT_VALUE));
411       ccc_value.handle = p_clcb->s_handle;
412       ccc_value.len = 2;
413       ccc_value.value[0] = GATT_CLT_CONFIG_INDICATION;
414       GATTC_Write(p_clcb->conn_id, GATT_WRITE, &ccc_value);
415       break;
416     }
417   }
418 }
419 
420 /*******************************************************************************
421  *
422  * Function         GATT_ConfigServiceChangeCCC
423  *
424  * Description      Configure service change indication on remote device
425  *
426  * Returns          none
427  *
428  ******************************************************************************/
GATT_ConfigServiceChangeCCC(const RawAddress & remote_bda,bool enable,tBT_TRANSPORT transport)429 void GATT_ConfigServiceChangeCCC(const RawAddress& remote_bda, bool enable,
430                                  tBT_TRANSPORT transport) {
431   tGATT_PROFILE_CLCB* p_clcb =
432       gatt_profile_find_clcb_by_bd_addr(remote_bda, transport);
433 
434   if (p_clcb == NULL)
435     p_clcb = gatt_profile_clcb_alloc(0, remote_bda, transport);
436 
437   if (p_clcb == NULL) return;
438 
439   if (GATT_GetConnIdIfConnected(gatt_cb.gatt_if, remote_bda, &p_clcb->conn_id,
440                                 transport)) {
441     p_clcb->connected = true;
442   }
443   /* hold the link here */
444   GATT_Connect(gatt_cb.gatt_if, remote_bda, true, transport, true);
445   p_clcb->ccc_stage = GATT_SVC_CHANGED_CONNECTING;
446 
447   if (!p_clcb->connected) {
448     /* wait for connection */
449     return;
450   }
451 
452   p_clcb->ccc_stage++;
453   gatt_cl_start_config_ccc(p_clcb);
454 }
455