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