• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /******************************************************************************
2  *
3  *  Copyright 1999-2013 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 #include "bt_target.h"
20 #include "gatt_api.h"
21 #include "gatt_int.h"
22 #include "osi/include/allocator.h"
23 #include "osi/include/osi.h"
24 #include "srvc_dis_int.h"
25 #include "srvc_eng_int.h"
26 #include "types/bluetooth/uuid.h"
27 #include "types/raw_address.h"
28 
29 #include <base/logging.h>
30 
31 using base::StringPrintf;
32 static void srvc_eng_s_request_cback(uint16_t conn_id, uint32_t trans_id,
33                                      tGATTS_REQ_TYPE type, tGATTS_DATA* p_data);
34 static void srvc_eng_connect_cback(UNUSED_ATTR tGATT_IF gatt_if,
35                                    const RawAddress& bda, uint16_t conn_id,
36                                    bool connected, tGATT_DISCONN_REASON reason,
37                                    tBT_TRANSPORT transport);
38 static void srvc_eng_c_cmpl_cback(uint16_t conn_id, tGATTC_OPTYPE op,
39                                   tGATT_STATUS status,
40                                   tGATT_CL_COMPLETE* p_data);
41 
42 static tGATT_CBACK srvc_gatt_cback = {
43     .p_conn_cb = srvc_eng_connect_cback,
44     .p_cmpl_cb = srvc_eng_c_cmpl_cback,
45     .p_disc_res_cb = nullptr,
46     .p_disc_cmpl_cb = nullptr,
47     .p_req_cb = srvc_eng_s_request_cback,
48     .p_enc_cmpl_cb = nullptr,
49     .p_congestion_cb = nullptr,
50     .p_phy_update_cb = nullptr,
51     .p_conn_update_cb = nullptr,
52     .p_subrate_chg_cb = nullptr,
53 };
54 
55 /* type for action functions */
56 typedef void (*tSRVC_ENG_C_CMPL_ACTION)(tSRVC_CLCB* p_clcb, tGATTC_OPTYPE op,
57                                         tGATT_STATUS status,
58                                         tGATT_CL_COMPLETE* p_data);
59 
60 static const tSRVC_ENG_C_CMPL_ACTION srvc_eng_c_cmpl_act[SRVC_ID_MAX] = {
61     dis_c_cmpl_cback,
62 };
63 
64 tSRVC_ENG_CB srvc_eng_cb;
65 
66 /*******************************************************************************
67  *
68  * Function         srvc_eng_find_clcb_by_bd_addr
69  *
70  * Description      The function searches all LCBs with macthing bd address.
71  *
72  * Returns          Pointer to the found link conenction control block.
73  *
74  ******************************************************************************/
srvc_eng_find_clcb_by_bd_addr(const RawAddress & bda)75 static tSRVC_CLCB* srvc_eng_find_clcb_by_bd_addr(const RawAddress& bda) {
76   uint8_t i_clcb;
77   tSRVC_CLCB* p_clcb = NULL;
78 
79   for (i_clcb = 0, p_clcb = srvc_eng_cb.clcb; i_clcb < SRVC_MAX_APPS;
80        i_clcb++, p_clcb++) {
81     if (p_clcb->in_use && p_clcb->connected && p_clcb->bda == bda) {
82       return p_clcb;
83     }
84   }
85 
86   return NULL;
87 }
88 /*******************************************************************************
89  *
90  * Function         srvc_eng_find_clcb_by_conn_id
91  *
92  * Description      The function searches all LCBs with macthing connection ID.
93  *
94  * Returns          Pointer to the found link conenction control block.
95  *
96  ******************************************************************************/
srvc_eng_find_clcb_by_conn_id(uint16_t conn_id)97 tSRVC_CLCB* srvc_eng_find_clcb_by_conn_id(uint16_t conn_id) {
98   uint8_t i_clcb;
99   tSRVC_CLCB* p_clcb = NULL;
100 
101   for (i_clcb = 0, p_clcb = srvc_eng_cb.clcb; i_clcb < SRVC_MAX_APPS;
102        i_clcb++, p_clcb++) {
103     if (p_clcb->in_use && p_clcb->connected && p_clcb->conn_id == conn_id) {
104       return p_clcb;
105     }
106   }
107 
108   return NULL;
109 }
110 /*******************************************************************************
111  *
112  * Function         srvc_eng_find_clcb_by_conn_id
113  *
114  * Description      The function searches all LCBs with macthing connection ID.
115  *
116  * Returns          Pointer to the found link conenction control block.
117  *
118  ******************************************************************************/
srvc_eng_find_clcb_idx_by_conn_id(uint16_t conn_id)119 static uint8_t srvc_eng_find_clcb_idx_by_conn_id(uint16_t conn_id) {
120   uint8_t i_clcb;
121   tSRVC_CLCB* p_clcb = NULL;
122 
123   for (i_clcb = 0, p_clcb = srvc_eng_cb.clcb; i_clcb < SRVC_MAX_APPS;
124        i_clcb++, p_clcb++) {
125     if (p_clcb->in_use && p_clcb->connected && p_clcb->conn_id == conn_id) {
126       return i_clcb;
127     }
128   }
129 
130   return SRVC_MAX_APPS;
131 }
132 /*******************************************************************************
133  *
134  * Function         srvc_eng_clcb_alloc
135  *
136  * Description      Allocate a GATT profile connection link control block
137  *
138  * Returns          NULL if not found. Otherwise pointer to the connection link
139  *                  block.
140  *
141  ******************************************************************************/
srvc_eng_clcb_alloc(uint16_t conn_id,const RawAddress & bda)142 static tSRVC_CLCB* srvc_eng_clcb_alloc(uint16_t conn_id,
143                                        const RawAddress& bda) {
144   uint8_t i_clcb = 0;
145   tSRVC_CLCB* p_clcb = NULL;
146 
147   for (i_clcb = 0, p_clcb = srvc_eng_cb.clcb; i_clcb < SRVC_MAX_APPS;
148        i_clcb++, p_clcb++) {
149     if (!p_clcb->in_use) {
150       p_clcb->in_use = true;
151       p_clcb->conn_id = conn_id;
152       p_clcb->connected = true;
153       p_clcb->bda = bda;
154       break;
155     }
156   }
157   return p_clcb;
158 }
159 /*******************************************************************************
160  *
161  * Function         srvc_eng_clcb_dealloc
162  *
163  * Description      De-allocate a GATT profile connection link control block
164  *
165  * Returns          True the deallocation is successful
166  *
167  ******************************************************************************/
srvc_eng_clcb_dealloc(uint16_t conn_id)168 static bool srvc_eng_clcb_dealloc(uint16_t conn_id) {
169   uint8_t i_clcb = 0;
170   tSRVC_CLCB* p_clcb = NULL;
171 
172   for (i_clcb = 0, p_clcb = srvc_eng_cb.clcb; i_clcb < SRVC_MAX_APPS;
173        i_clcb++, p_clcb++) {
174     if (p_clcb->in_use && p_clcb->connected && (p_clcb->conn_id == conn_id)) {
175       unsigned j;
176       for (j = 0; j < ARRAY_SIZE(p_clcb->dis_value.data_string); j++)
177         osi_free(p_clcb->dis_value.data_string[j]);
178 
179       memset(p_clcb, 0, sizeof(tSRVC_CLCB));
180       return true;
181     }
182   }
183   return false;
184 }
185 /*******************************************************************************
186  *   Service Engine Server Attributes Database Read/Read Blob Request process
187  ******************************************************************************/
srvc_eng_process_read_req(uint8_t clcb_idx,tGATT_READ_REQ * p_data,tGATTS_RSP * p_rsp,tGATT_STATUS * p_status)188 static uint8_t srvc_eng_process_read_req(uint8_t clcb_idx,
189                                          tGATT_READ_REQ* p_data,
190                                          tGATTS_RSP* p_rsp,
191                                          tGATT_STATUS* p_status) {
192   tGATT_STATUS status = GATT_NOT_FOUND;
193   uint8_t act = SRVC_ACT_RSP;
194 
195   if (p_data->is_long) p_rsp->attr_value.offset = p_data->offset;
196 
197   p_rsp->attr_value.handle = p_data->handle;
198 
199   if (dis_valid_handle_range(p_data->handle))
200     act = dis_read_attr_value(clcb_idx, p_data->handle, &p_rsp->attr_value,
201                               p_data->is_long, p_status);
202   else
203     *p_status = status;
204   return act;
205 }
206 /*******************************************************************************
207  *   Service Engine Server Attributes Database write Request process
208  ******************************************************************************/
srvc_eng_process_write_req(uint8_t clcb_idx,tGATT_WRITE_REQ * p_data,UNUSED_ATTR tGATTS_RSP * p_rsp,tGATT_STATUS * p_status)209 static uint8_t srvc_eng_process_write_req(uint8_t clcb_idx,
210                                           tGATT_WRITE_REQ* p_data,
211                                           UNUSED_ATTR tGATTS_RSP* p_rsp,
212                                           tGATT_STATUS* p_status) {
213   uint8_t act = SRVC_ACT_RSP;
214 
215   if (dis_valid_handle_range(p_data->handle)) {
216     act = dis_write_attr_value(p_data, p_status);
217   } else
218     *p_status = GATT_NOT_FOUND;
219 
220   return act;
221 }
222 
223 /*******************************************************************************
224  *
225  * Function         srvc_eng_s_request_cback
226  *
227  * Description      GATT DIS attribute access request callback.
228  *
229  * Returns          void.
230  *
231  ******************************************************************************/
srvc_eng_s_request_cback(uint16_t conn_id,uint32_t trans_id,tGATTS_REQ_TYPE type,tGATTS_DATA * p_data)232 static void srvc_eng_s_request_cback(uint16_t conn_id, uint32_t trans_id,
233                                      tGATTS_REQ_TYPE type,
234                                      tGATTS_DATA* p_data) {
235   tGATT_STATUS status = GATT_INVALID_PDU;
236   tGATTS_RSP rsp_msg;
237   uint8_t act = SRVC_ACT_IGNORE;
238   uint8_t clcb_idx = srvc_eng_find_clcb_idx_by_conn_id(conn_id);
239 
240   VLOG(1) << StringPrintf("srvc_eng_s_request_cback : recv type (0x%02x)",
241                           type);
242 
243   memset(&rsp_msg, 0, sizeof(tGATTS_RSP));
244 
245   srvc_eng_cb.clcb[clcb_idx].trans_id = trans_id;
246 
247   switch (type) {
248     case GATTS_REQ_TYPE_READ_CHARACTERISTIC:
249     case GATTS_REQ_TYPE_READ_DESCRIPTOR:
250       act = srvc_eng_process_read_req(clcb_idx, &p_data->read_req, &rsp_msg,
251                                       &status);
252       break;
253 
254     case GATTS_REQ_TYPE_WRITE_CHARACTERISTIC:
255     case GATTS_REQ_TYPE_WRITE_DESCRIPTOR:
256       act = srvc_eng_process_write_req(clcb_idx, &p_data->write_req, &rsp_msg,
257                                        &status);
258       if (!p_data->write_req.need_rsp) act = SRVC_ACT_IGNORE;
259       break;
260 
261     case GATTS_REQ_TYPE_WRITE_EXEC:
262       VLOG(1) << "Ignore GATT_REQ_EXEC_WRITE/WRITE_CMD";
263       break;
264 
265     case GATTS_REQ_TYPE_MTU:
266       VLOG(1) << "Get MTU exchange new mtu size: " << p_data->mtu;
267       break;
268 
269     default:
270       VLOG(1) << StringPrintf("Unknown/unexpected LE GAP ATT request: 0x%02x",
271                               type);
272       break;
273   }
274 
275   srvc_eng_cb.clcb[clcb_idx].trans_id = 0;
276 
277   if (act == SRVC_ACT_RSP) GATTS_SendRsp(conn_id, trans_id, status, &rsp_msg);
278 }
279 
280 /*******************************************************************************
281  *
282  * Function         srvc_eng_c_cmpl_cback
283  *
284  * Description      Client operation complete callback.
285  *
286  * Returns          void
287  *
288  ******************************************************************************/
srvc_eng_c_cmpl_cback(uint16_t conn_id,tGATTC_OPTYPE op,tGATT_STATUS status,tGATT_CL_COMPLETE * p_data)289 static void srvc_eng_c_cmpl_cback(uint16_t conn_id, tGATTC_OPTYPE op,
290                                   tGATT_STATUS status,
291                                   tGATT_CL_COMPLETE* p_data) {
292   tSRVC_CLCB* p_clcb = srvc_eng_find_clcb_by_conn_id(conn_id);
293 
294   VLOG(1) << StringPrintf(
295       "srvc_eng_c_cmpl_cback() - op_code: 0x%02x  status: 0x%02x ", op, status);
296 
297   if (p_clcb == NULL) {
298     LOG(ERROR) << __func__ << " received for unknown connection";
299     return;
300   }
301 
302   if (p_clcb->cur_srvc_id != SRVC_ID_NONE && p_clcb->cur_srvc_id <= SRVC_ID_MAX)
303     srvc_eng_c_cmpl_act[p_clcb->cur_srvc_id - 1](p_clcb, op, status, p_data);
304 }
305 
306 /*******************************************************************************
307  *
308  * Function         srvc_eng_connect_cback
309  *
310  * Description      Gatt profile connection callback.
311  *
312  * Returns          void
313  *
314  ******************************************************************************/
srvc_eng_connect_cback(UNUSED_ATTR tGATT_IF gatt_if,const RawAddress & bda,uint16_t conn_id,bool connected,tGATT_DISCONN_REASON reason,UNUSED_ATTR tBT_TRANSPORT transport)315 static void srvc_eng_connect_cback(UNUSED_ATTR tGATT_IF gatt_if,
316                                    const RawAddress& bda, uint16_t conn_id,
317                                    bool connected, tGATT_DISCONN_REASON reason,
318                                    UNUSED_ATTR tBT_TRANSPORT transport) {
319   VLOG(1) << __func__ << ": from " << ADDRESS_TO_LOGGABLE_STR(bda)
320           << StringPrintf(" connected:%d conn_id=%d", connected, conn_id);
321 
322   if (connected) {
323     if (srvc_eng_clcb_alloc(conn_id, bda) == NULL) {
324       LOG(ERROR) << __func__ << "srvc_eng_connect_cback: no_resource";
325       return;
326     }
327   } else {
328     srvc_eng_clcb_dealloc(conn_id);
329   }
330 }
331 /*******************************************************************************
332  *
333  * Function         srvc_eng_c_cmpl_cback
334  *
335  * Description      Client operation complete callback.
336  *
337  * Returns          void
338  *
339  ******************************************************************************/
srvc_eng_request_channel(const RawAddress & remote_bda,uint8_t srvc_id)340 bool srvc_eng_request_channel(const RawAddress& remote_bda, uint8_t srvc_id) {
341   bool set = true;
342   tSRVC_CLCB* p_clcb = srvc_eng_find_clcb_by_bd_addr(remote_bda);
343 
344   if (p_clcb == NULL) p_clcb = srvc_eng_clcb_alloc(0, remote_bda);
345 
346   if (p_clcb && p_clcb->cur_srvc_id == SRVC_ID_NONE)
347     p_clcb->cur_srvc_id = srvc_id;
348   else
349     set = false;
350 
351   return set;
352 }
353 /*******************************************************************************
354  *
355  * Function         srvc_eng_release_channel
356  *
357  * Description      Client operation complete callback.
358  *
359  * Returns          void
360  *
361  ******************************************************************************/
srvc_eng_release_channel(uint16_t conn_id)362 void srvc_eng_release_channel(uint16_t conn_id) {
363   tSRVC_CLCB* p_clcb = srvc_eng_find_clcb_by_conn_id(conn_id);
364 
365   if (p_clcb == NULL) {
366     LOG(ERROR) << __func__ << ": invalid connection id " << conn_id;
367     return;
368   }
369 
370   p_clcb->cur_srvc_id = SRVC_ID_NONE;
371 
372   /* check pending request */
373   GATT_Disconnect(p_clcb->conn_id);
374 }
375 /*******************************************************************************
376  *
377  * Function         srvc_eng_init
378  *
379  * Description      Initializa the GATT Service engine.
380  *
381  ******************************************************************************/
srvc_eng_init(void)382 tGATT_STATUS srvc_eng_init(void) {
383 
384   if (srvc_eng_cb.enabled) {
385     LOG(ERROR) << "DIS already initalized";
386   } else {
387     memset(&srvc_eng_cb, 0, sizeof(tSRVC_ENG_CB));
388 
389     /* Create a GATT profile service */
390     bluetooth::Uuid app_uuid =
391         bluetooth::Uuid::From16Bit(UUID_SERVCLASS_DEVICE_INFO);
392     srvc_eng_cb.gatt_if =
393         GATT_Register(app_uuid, "GattServiceEngine", &srvc_gatt_cback, false);
394     GATT_StartIf(srvc_eng_cb.gatt_if);
395 
396     VLOG(1) << "Srvc_Init:  gatt_if=" << +srvc_eng_cb.gatt_if;
397 
398     srvc_eng_cb.enabled = true;
399     dis_cb.dis_read_uuid_idx = 0xff;
400   }
401   return GATT_SUCCESS;
402 }
403