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