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