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 namespace bluetooth;
31 
32 static void srvc_eng_connect_cback(tGATT_IF /* gatt_if */, const RawAddress& bda, tCONN_ID conn_id,
33                                    bool connected, tGATT_DISCONN_REASON reason,
34                                    tBT_TRANSPORT transport);
35 static void srvc_eng_c_cmpl_cback(tCONN_ID conn_id, tGATTC_OPTYPE op, tGATT_STATUS status,
36                                   tGATT_CL_COMPLETE* p_data);
37 
38 static tGATT_CBACK srvc_gatt_cback = {
39         .p_conn_cb = srvc_eng_connect_cback,
40         .p_cmpl_cb = srvc_eng_c_cmpl_cback,
41         .p_disc_res_cb = nullptr,
42         .p_disc_cmpl_cb = nullptr,
43         .p_req_cb = nullptr,
44         .p_enc_cmpl_cb = nullptr,
45         .p_congestion_cb = nullptr,
46         .p_phy_update_cb = nullptr,
47         .p_conn_update_cb = nullptr,
48         .p_subrate_chg_cb = nullptr,
49 };
50 
51 /* type for action functions */
52 typedef void (*tSRVC_ENG_C_CMPL_ACTION)(tSRVC_CLCB* p_clcb, tGATTC_OPTYPE op, tGATT_STATUS status,
53                                         tGATT_CL_COMPLETE* p_data);
54 
55 static const tSRVC_ENG_C_CMPL_ACTION srvc_eng_c_cmpl_act[SRVC_ID_MAX] = {
56         dis_c_cmpl_cback,
57 };
58 
59 tSRVC_ENG_CB srvc_eng_cb;
60 
61 /*******************************************************************************
62  *
63  * Function         srvc_eng_find_clcb_by_bd_addr
64  *
65  * Description      The function searches all LCBs with macthing bd address.
66  *
67  * Returns          Pointer to the found link conenction control block.
68  *
69  ******************************************************************************/
srvc_eng_find_clcb_by_bd_addr(const RawAddress & bda)70 static tSRVC_CLCB* srvc_eng_find_clcb_by_bd_addr(const RawAddress& bda) {
71   uint8_t i_clcb;
72   tSRVC_CLCB* p_clcb = NULL;
73 
74   for (i_clcb = 0, p_clcb = srvc_eng_cb.clcb; i_clcb < SRVC_MAX_APPS; i_clcb++, p_clcb++) {
75     if (p_clcb->in_use && p_clcb->connected && p_clcb->bda == bda) {
76       return p_clcb;
77     }
78   }
79 
80   return NULL;
81 }
82 /*******************************************************************************
83  *
84  * Function         srvc_eng_find_clcb_by_conn_id
85  *
86  * Description      The function searches all LCBs with macthing connection ID.
87  *
88  * Returns          Pointer to the found link conenction control block.
89  *
90  ******************************************************************************/
srvc_eng_find_clcb_by_conn_id(tCONN_ID conn_id)91 tSRVC_CLCB* srvc_eng_find_clcb_by_conn_id(tCONN_ID conn_id) {
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; i_clcb++, p_clcb++) {
96     if (p_clcb->in_use && p_clcb->connected && p_clcb->conn_id == conn_id) {
97       return p_clcb;
98     }
99   }
100 
101   return NULL;
102 }
103 /*******************************************************************************
104  *
105  * Function         srvc_eng_clcb_alloc
106  *
107  * Description      Allocate a GATT profile connection link control block
108  *
109  * Returns          NULL if not found. Otherwise pointer to the connection link
110  *                  block.
111  *
112  ******************************************************************************/
srvc_eng_clcb_alloc(tCONN_ID conn_id,const RawAddress & bda)113 static tSRVC_CLCB* srvc_eng_clcb_alloc(tCONN_ID conn_id, const RawAddress& bda) {
114   uint8_t i_clcb = 0;
115   tSRVC_CLCB* p_clcb = NULL;
116 
117   for (i_clcb = 0, p_clcb = srvc_eng_cb.clcb; i_clcb < SRVC_MAX_APPS; i_clcb++, p_clcb++) {
118     if (!p_clcb->in_use) {
119       p_clcb->in_use = true;
120       p_clcb->conn_id = conn_id;
121       p_clcb->connected = true;
122       p_clcb->bda = bda;
123       break;
124     }
125   }
126   return p_clcb;
127 }
128 /*******************************************************************************
129  *
130  * Function         srvc_eng_clcb_dealloc
131  *
132  * Description      De-allocate a GATT profile connection link control block
133  *
134  * Returns          True the deallocation is successful
135  *
136  ******************************************************************************/
srvc_eng_clcb_dealloc(tCONN_ID conn_id)137 static bool srvc_eng_clcb_dealloc(tCONN_ID conn_id) {
138   uint8_t i_clcb = 0;
139   tSRVC_CLCB* p_clcb = NULL;
140 
141   for (i_clcb = 0, p_clcb = srvc_eng_cb.clcb; i_clcb < SRVC_MAX_APPS; i_clcb++, p_clcb++) {
142     if (p_clcb->in_use && p_clcb->connected && (p_clcb->conn_id == conn_id)) {
143       unsigned j;
144       for (j = 0; j < ARRAY_SIZE(p_clcb->dis_value.data_string); j++) {
145         osi_free(p_clcb->dis_value.data_string[j]);
146       }
147 
148       memset(p_clcb, 0, sizeof(tSRVC_CLCB));
149       return true;
150     }
151   }
152   return false;
153 }
154 
155 /*******************************************************************************
156  *
157  * Function         srvc_eng_c_cmpl_cback
158  *
159  * Description      Client operation complete callback.
160  *
161  * Returns          void
162  *
163  ******************************************************************************/
srvc_eng_c_cmpl_cback(tCONN_ID conn_id,tGATTC_OPTYPE op,tGATT_STATUS status,tGATT_CL_COMPLETE * p_data)164 static void srvc_eng_c_cmpl_cback(tCONN_ID conn_id, tGATTC_OPTYPE op, tGATT_STATUS status,
165                                   tGATT_CL_COMPLETE* p_data) {
166   tSRVC_CLCB* p_clcb = srvc_eng_find_clcb_by_conn_id(conn_id);
167 
168   log::verbose("srvc_eng_c_cmpl_cback() - op_code: 0x{:02x}  status: 0x{:02x}", op, status);
169 
170   if (p_clcb == NULL) {
171     log::error("received for unknown connection");
172     return;
173   }
174 
175   if (p_clcb->cur_srvc_id != SRVC_ID_NONE && p_clcb->cur_srvc_id <= SRVC_ID_MAX) {
176     srvc_eng_c_cmpl_act[p_clcb->cur_srvc_id - 1](p_clcb, op, status, p_data);
177   }
178 }
179 
180 /*******************************************************************************
181  *
182  * Function         srvc_eng_connect_cback
183  *
184  * Description      Gatt profile connection callback.
185  *
186  * Returns          void
187  *
188  ******************************************************************************/
srvc_eng_connect_cback(tGATT_IF,const RawAddress & bda,tCONN_ID conn_id,bool connected,tGATT_DISCONN_REASON,tBT_TRANSPORT)189 static void srvc_eng_connect_cback(tGATT_IF /* gatt_if */, const RawAddress& bda, tCONN_ID conn_id,
190                                    bool connected, tGATT_DISCONN_REASON /* reason */,
191                                    tBT_TRANSPORT /* transport */) {
192   log::verbose("from {} connected:{} conn_id={}", bda, connected, conn_id);
193 
194   if (connected) {
195     if (srvc_eng_clcb_alloc(conn_id, bda) == NULL) {
196       log::error("srvc_eng_connect_cback: no_resource");
197       return;
198     }
199   } else {
200     srvc_eng_clcb_dealloc(conn_id);
201   }
202 }
203 /*******************************************************************************
204  *
205  * Function         srvc_eng_c_cmpl_cback
206  *
207  * Description      Client operation complete callback.
208  *
209  * Returns          void
210  *
211  ******************************************************************************/
srvc_eng_request_channel(const RawAddress & remote_bda,uint8_t srvc_id)212 bool srvc_eng_request_channel(const RawAddress& remote_bda, uint8_t srvc_id) {
213   bool set = true;
214   tSRVC_CLCB* p_clcb = srvc_eng_find_clcb_by_bd_addr(remote_bda);
215 
216   if (p_clcb == NULL) {
217     p_clcb = srvc_eng_clcb_alloc(0, remote_bda);
218   }
219 
220   if (p_clcb && p_clcb->cur_srvc_id == SRVC_ID_NONE) {
221     p_clcb->cur_srvc_id = srvc_id;
222   } else {
223     set = false;
224   }
225 
226   return set;
227 }
228 /*******************************************************************************
229  *
230  * Function         srvc_eng_release_channel
231  *
232  * Description      Client operation complete callback.
233  *
234  * Returns          void
235  *
236  ******************************************************************************/
srvc_eng_release_channel(tCONN_ID conn_id)237 void srvc_eng_release_channel(tCONN_ID conn_id) {
238   tSRVC_CLCB* p_clcb = srvc_eng_find_clcb_by_conn_id(conn_id);
239 
240   if (p_clcb == NULL) {
241     log::error("invalid connection id {}", conn_id);
242     return;
243   }
244 
245   p_clcb->cur_srvc_id = SRVC_ID_NONE;
246 
247   /* check pending request */
248   if (GATT_Disconnect(p_clcb->conn_id) != GATT_SUCCESS) {
249     log::warn("Unable to disconnect GATT conn_id:{}", p_clcb->conn_id);
250   }
251 }
252 /*******************************************************************************
253  *
254  * Function         srvc_eng_init
255  *
256  * Description      Initialize the GATT Service engine.
257  *
258  ******************************************************************************/
srvc_eng_init(void)259 tGATT_STATUS srvc_eng_init(void) {
260   if (srvc_eng_cb.enabled) {
261     log::verbose("DIS already initialized");
262   } else {
263     memset(&srvc_eng_cb, 0, sizeof(tSRVC_ENG_CB));
264 
265     /* Create a GATT profile service */
266     bluetooth::Uuid app_uuid = bluetooth::Uuid::From16Bit(UUID_SERVCLASS_DEVICE_INFO);
267     srvc_eng_cb.gatt_if = GATT_Register(app_uuid, "GattServiceEngine", &srvc_gatt_cback, false);
268     GATT_StartIf(srvc_eng_cb.gatt_if);
269 
270     log::verbose("Srvc_Init:  gatt_if={}", srvc_eng_cb.gatt_if);
271 
272     srvc_eng_cb.enabled = true;
273     dis_cb.dis_read_uuid_idx = 0xff;
274   }
275   return GATT_SUCCESS;
276 }
277