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