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