1 /******************************************************************************
2 *
3 * Copyright (C) 1999-2012 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 /******************************************************************************
20 *
21 * this file contains the main GATT client functions
22 *
23 ******************************************************************************/
24
25 #include "bt_target.h"
26
27 #if BLE_INCLUDED == TRUE
28
29 #include <string.h>
30 #include "gki.h"
31 #include "gatt_int.h"
32
33 #define GATT_WRITE_LONG_HDR_SIZE 5 /* 1 opcode + 2 handle + 2 offset */
34 #define GATT_READ_CHAR_VALUE_HDL (GATT_READ_CHAR_VALUE | 0x80)
35 #define GATT_READ_INC_SRV_UUID128 (GATT_DISC_INC_SRVC | 0x90)
36
37 /********************************************************************************
38 ** G L O B A L G A T T D A T A *
39 *********************************************************************************/
40 void gatt_send_prepare_write(tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb);
41
42 UINT8 disc_type_to_att_opcode[GATT_DISC_MAX] =
43 {
44 0,
45 GATT_REQ_READ_BY_GRP_TYPE, /* GATT_DISC_SRVC_ALL = 1, */
46 GATT_REQ_FIND_TYPE_VALUE, /* GATT_DISC_SRVC_BY_UUID, */
47 GATT_REQ_READ_BY_TYPE, /* GATT_DISC_INC_SRVC, */
48 GATT_REQ_READ_BY_TYPE, /* GATT_DISC_CHAR, */
49 GATT_REQ_FIND_INFO /* GATT_DISC_CHAR_DSCPT, */
50 };
51
52 UINT16 disc_type_to_uuid[GATT_DISC_MAX] =
53 {
54 0, /* reserved */
55 GATT_UUID_PRI_SERVICE, /* <service> DISC_SRVC_ALL */
56 GATT_UUID_PRI_SERVICE, /* <service> for DISC_SERVC_BY_UUID */
57 GATT_UUID_INCLUDE_SERVICE, /* <include_service> for DISC_INC_SRVC */
58 GATT_UUID_CHAR_DECLARE, /* <characteristic> for DISC_CHAR */
59 0 /* no type filtering for DISC_CHAR_DSCPT */
60 };
61
62
63 /*******************************************************************************
64 **
65 ** Function gatt_act_discovery
66 **
67 ** Description GATT discovery operation.
68 **
69 ** Returns void.
70 **
71 *******************************************************************************/
gatt_act_discovery(tGATT_CLCB * p_clcb)72 void gatt_act_discovery(tGATT_CLCB *p_clcb)
73 {
74 UINT8 op_code = disc_type_to_att_opcode[p_clcb->op_subtype];
75 tGATT_CL_MSG cl_req;
76
77 if (p_clcb->s_handle <= p_clcb->e_handle && p_clcb->s_handle != 0)
78 {
79 memset(&cl_req, 0, sizeof(tGATT_CL_MSG));
80
81 cl_req.browse.s_handle = p_clcb->s_handle;
82 cl_req.browse.e_handle = p_clcb->e_handle;
83
84 if (disc_type_to_uuid[p_clcb->op_subtype] != 0)
85 {
86 cl_req.browse.uuid.len = 2;
87 cl_req.browse.uuid.uu.uuid16 = disc_type_to_uuid[p_clcb->op_subtype];
88 }
89
90 if (p_clcb->op_subtype == GATT_DISC_SRVC_BY_UUID) /* fill in the FindByTypeValue request info*/
91 {
92 cl_req.find_type_value.uuid.len = 2;
93 cl_req.find_type_value.uuid.uu.uuid16 = disc_type_to_uuid[p_clcb->op_subtype];
94 cl_req.find_type_value.s_handle = p_clcb->s_handle;
95 cl_req.find_type_value.e_handle = p_clcb->e_handle;
96 cl_req.find_type_value.value_len = p_clcb->uuid.len;
97 memcpy (cl_req.find_type_value.value, &p_clcb->uuid.uu, p_clcb->uuid.len);
98 }
99
100 if (attp_send_cl_msg(p_clcb->p_tcb, p_clcb->clcb_idx, op_code, &cl_req) != GATT_SUCCESS)
101 {
102 gatt_end_operation(p_clcb, GATT_ERROR, NULL);
103 }
104 }
105 else /* end of handle range */
106 gatt_end_operation(p_clcb, GATT_SUCCESS, NULL);
107 }
108
109 /*******************************************************************************
110 **
111 ** Function gatt_act_read
112 **
113 ** Description GATT read operation.
114 **
115 ** Returns void.
116 **
117 *******************************************************************************/
gatt_act_read(tGATT_CLCB * p_clcb,UINT16 offset)118 void gatt_act_read (tGATT_CLCB *p_clcb, UINT16 offset)
119 {
120 tGATT_TCB *p_tcb = p_clcb->p_tcb;
121 UINT8 rt = GATT_INTERNAL_ERROR;
122 tGATT_CL_MSG msg;
123 UINT8 op_code = 0;
124
125 memset (&msg, 0, sizeof(tGATT_CL_MSG));
126
127 switch (p_clcb->op_subtype)
128 {
129 case GATT_READ_CHAR_VALUE:
130 case GATT_READ_BY_TYPE:
131 op_code = GATT_REQ_READ_BY_TYPE;
132 msg.browse.s_handle = p_clcb->s_handle;
133 msg.browse.e_handle = p_clcb->e_handle;
134 if (p_clcb->op_subtype == GATT_READ_BY_TYPE)
135 memcpy(&msg.browse.uuid, &p_clcb->uuid, sizeof(tBT_UUID));
136 else
137 {
138 msg.browse.uuid.len = LEN_UUID_16;
139 msg.browse.uuid.uu.uuid16 = GATT_UUID_CHAR_DECLARE;
140 }
141 break;
142
143 case GATT_READ_CHAR_VALUE_HDL:
144 case GATT_READ_BY_HANDLE:
145 if (!p_clcb->counter)
146 {
147 op_code = GATT_REQ_READ;
148 msg.handle = p_clcb->s_handle;
149 }
150 else
151 {
152 if (!p_clcb->first_read_blob_after_read)
153 p_clcb->first_read_blob_after_read = TRUE;
154 else
155 p_clcb->first_read_blob_after_read = FALSE;
156
157 GATT_TRACE_DEBUG1("gatt_act_read first_read_blob_after_read=%d",
158 p_clcb->first_read_blob_after_read);
159 op_code = GATT_REQ_READ_BLOB;
160 msg.read_blob.offset = offset;
161 msg.read_blob.handle = p_clcb->s_handle;
162 }
163 p_clcb->op_subtype &= ~ 0x80;
164 break;
165
166 case GATT_READ_PARTIAL:
167 op_code = GATT_REQ_READ_BLOB;
168 msg.read_blob.handle = p_clcb->s_handle;
169 msg.read_blob.offset = offset;
170 break;
171
172 case GATT_READ_MULTIPLE:
173 op_code = GATT_REQ_READ_MULTI;
174 memcpy (&msg.read_multi, p_clcb->p_attr_buf, sizeof(tGATT_READ_MULTI));
175 break;
176
177 case GATT_READ_INC_SRV_UUID128:
178 op_code = GATT_REQ_READ;
179 msg.handle = p_clcb->s_handle;
180 p_clcb->op_subtype &= ~ 0x90;
181 break;
182
183 default:
184 GATT_TRACE_ERROR1("Unknown read type: %d", p_clcb->op_subtype);
185 break;
186 }
187
188 if ( op_code == 0 ||
189 (rt = attp_send_cl_msg(p_tcb, p_clcb->clcb_idx, op_code, &msg)) != GATT_SUCCESS)
190 {
191 gatt_end_operation(p_clcb, rt, NULL);
192 }
193 }
194
195 /*******************************************************************************
196 **
197 ** Function gatt_act_write
198 **
199 ** Description GATT write operation.
200 **
201 ** Returns void.
202 **
203 *******************************************************************************/
gatt_act_write(tGATT_CLCB * p_clcb,UINT8 sec_act)204 void gatt_act_write (tGATT_CLCB *p_clcb, UINT8 sec_act)
205 {
206 tGATT_TCB *p_tcb = p_clcb->p_tcb;
207 UINT8 rt = GATT_SUCCESS, op_code;
208 tGATT_VALUE *p_attr = (tGATT_VALUE *)p_clcb->p_attr_buf;
209
210 if (p_attr)
211 {
212 switch (p_clcb->op_subtype)
213 {
214 case GATT_WRITE_NO_RSP:
215 p_clcb->s_handle = p_attr->handle;
216 op_code = (sec_act == GATT_SEC_SIGN_DATA) ? GATT_SIGN_CMD_WRITE : GATT_CMD_WRITE;
217 rt = gatt_send_write_msg(p_tcb,
218 p_clcb->clcb_idx,
219 op_code,
220 p_attr->handle,
221 p_attr->len,
222 0,
223 p_attr->value);
224 break;
225
226 case GATT_WRITE:
227 if (p_attr->len <= (p_tcb->payload_size - GATT_HDR_SIZE))
228 {
229 p_clcb->s_handle = p_attr->handle;
230
231 rt = gatt_send_write_msg(p_tcb,
232 p_clcb->clcb_idx,
233 GATT_REQ_WRITE,
234 p_attr->handle,
235 p_attr->len,
236 0,
237 p_attr->value);
238 }
239 else /* prepare write for long attribute */
240 {
241 gatt_send_prepare_write(p_tcb, p_clcb);
242 }
243 break;
244
245 case GATT_WRITE_PREPARE:
246 gatt_send_prepare_write(p_tcb, p_clcb);
247 break;
248
249 default:
250 rt = GATT_INTERNAL_ERROR;
251 GATT_TRACE_ERROR1("Unknown write type: %d", p_clcb->op_subtype);
252 break;
253 }
254 }
255 else
256 rt = GATT_INTERNAL_ERROR;
257
258 if ((rt != GATT_SUCCESS && rt != GATT_CMD_STARTED)
259 || (rt != GATT_CMD_STARTED && p_clcb->op_subtype == GATT_WRITE_NO_RSP))
260 {
261 if (rt != GATT_SUCCESS)
262 {
263 GATT_TRACE_ERROR1("gatt_act_write() failed op_code=0x%x", op_code);
264 }
265 gatt_end_operation(p_clcb, rt, NULL);
266 }
267 }
268 /*******************************************************************************
269 **
270 ** Function gatt_send_queue_write_cancel
271 **
272 ** Description send queue write cancel
273 **
274 ** Returns void.
275 **
276 *******************************************************************************/
gatt_send_queue_write_cancel(tGATT_TCB * p_tcb,tGATT_CLCB * p_clcb,tGATT_EXEC_FLAG flag)277 void gatt_send_queue_write_cancel (tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb, tGATT_EXEC_FLAG flag)
278 {
279 UINT8 rt ;
280
281 GATT_TRACE_DEBUG0("gatt_send_queue_write_cancel ");
282
283 rt = attp_send_cl_msg(p_tcb, p_clcb->clcb_idx, GATT_REQ_EXEC_WRITE, (tGATT_CL_MSG *)&flag);
284
285 if (rt != GATT_SUCCESS)
286 {
287 gatt_end_operation(p_clcb, rt, NULL);
288 }
289 }
290 /*******************************************************************************
291 **
292 ** Function gatt_check_write_long_terminate
293 **
294 ** Description To terminate write long or not.
295 **
296 ** Returns TRUE: write long is terminated; FALSE keep sending.
297 **
298 *******************************************************************************/
gatt_check_write_long_terminate(tGATT_TCB * p_tcb,tGATT_CLCB * p_clcb,tGATT_VALUE * p_rsp_value)299 BOOLEAN gatt_check_write_long_terminate(tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb, tGATT_VALUE *p_rsp_value)
300 {
301 tGATT_VALUE *p_attr = (tGATT_VALUE *)p_clcb->p_attr_buf;
302 BOOLEAN exec = FALSE;
303 tGATT_EXEC_FLAG flag = GATT_PREP_WRITE_EXEC;
304
305 GATT_TRACE_DEBUG0("gatt_check_write_long_terminate ");
306 /* check the first write response status */
307 if (p_rsp_value != NULL)
308 {
309 if (p_rsp_value->handle != p_attr->handle ||
310 p_rsp_value->len != p_clcb->counter ||
311 memcmp(p_rsp_value->value, p_attr->value + p_attr->offset, p_rsp_value->len))
312 {
313 /* data does not match */
314 p_clcb->status = GATT_ERROR;
315 flag = GATT_PREP_WRITE_CANCEL;
316 exec = TRUE;
317 }
318 else /* response checking is good */
319 {
320 p_clcb->status = GATT_SUCCESS;
321 /* update write offset and check if end of attribute value */
322 if ((p_attr->offset += p_rsp_value->len) >= p_attr->len)
323 exec = TRUE;
324 }
325 }
326 if (exec)
327 {
328 gatt_send_queue_write_cancel (p_tcb, p_clcb, flag);
329 return TRUE;
330 }
331 return FALSE;
332 }
333 /*******************************************************************************
334 **
335 ** Function gatt_send_prepare_write
336 **
337 ** Description Send prepare write.
338 **
339 ** Returns void.
340 **
341 *******************************************************************************/
gatt_send_prepare_write(tGATT_TCB * p_tcb,tGATT_CLCB * p_clcb)342 void gatt_send_prepare_write(tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb)
343 {
344 tGATT_VALUE *p_attr = (tGATT_VALUE *)p_clcb->p_attr_buf;
345 UINT16 to_send, offset;
346 UINT8 rt = GATT_SUCCESS;
347 UINT8 type = p_clcb->op_subtype;
348
349 GATT_TRACE_DEBUG1("gatt_send_prepare_write type=0x%x", type );
350 to_send = p_attr->len - p_attr->offset;
351
352 if (to_send > (p_tcb->payload_size - GATT_WRITE_LONG_HDR_SIZE)) /* 2 = UINT16 offset bytes */
353 to_send = p_tcb->payload_size - GATT_WRITE_LONG_HDR_SIZE;
354
355 p_clcb->s_handle = p_attr->handle;
356
357 offset = p_attr->offset;
358 if (type == GATT_WRITE_PREPARE)
359 {
360 offset += p_clcb->start_offset;
361 }
362
363 GATT_TRACE_DEBUG2("offset =0x%x len=%d", offset, to_send );
364
365 rt = gatt_send_write_msg(p_tcb,
366 p_clcb->clcb_idx,
367 GATT_REQ_PREPARE_WRITE,
368 p_attr->handle,
369 to_send, /* length */
370 offset, /* used as offset */
371 p_attr->value + p_attr->offset); /* data */
372
373 /* remember the write long attribute length */
374 p_clcb->counter = to_send;
375
376 if (rt != GATT_SUCCESS )
377 {
378 gatt_end_operation(p_clcb, rt, NULL);
379 }
380 }
381
382
383 /*******************************************************************************
384 **
385 ** Function gatt_process_find_type_value_rsp
386 **
387 ** Description This function is called to handle find by type value response.
388 **
389 **
390 ** Returns void
391 **
392 *******************************************************************************/
gatt_process_find_type_value_rsp(tGATT_TCB * p_tcb,tGATT_CLCB * p_clcb,UINT16 len,UINT8 * p_data)393 void gatt_process_find_type_value_rsp (tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb, UINT16 len, UINT8 *p_data)
394 {
395 tGATT_DISC_RES result;
396 UINT8 *p = p_data;
397
398 GATT_TRACE_DEBUG0("gatt_process_find_type_value_rsp ");
399 /* unexpected response */
400 if (p_clcb->operation != GATTC_OPTYPE_DISCOVERY || p_clcb->op_subtype != GATT_DISC_SRVC_BY_UUID)
401 return;
402
403 memset (&result, 0, sizeof(tGATT_DISC_RES));
404 result.type.len = 2;
405 result.type.uu.uuid16 = GATT_UUID_PRI_SERVICE;
406
407 /* returns a series of handle ranges */
408 while (len >= 4)
409 {
410 STREAM_TO_UINT16 (result.handle, p);
411 STREAM_TO_UINT16 (result.value.group_value.e_handle, p);
412 memcpy (&result.value.group_value.service_type, &p_clcb->uuid, sizeof(tBT_UUID));
413
414 len -= 4;
415
416 if (p_clcb->p_reg->app_cb.p_disc_res_cb)
417 (*p_clcb->p_reg->app_cb.p_disc_res_cb)(p_clcb->conn_id, p_clcb->op_subtype, &result);
418 }
419
420 /* last handle + 1 */
421 p_clcb->s_handle = (result.value.group_value.e_handle == 0) ? 0 : (result.value.group_value.e_handle + 1);
422 /* initiate another request */
423 gatt_act_discovery(p_clcb) ;
424 }
425 /*******************************************************************************
426 **
427 ** Function gatt_process_read_info_rsp
428 **
429 ** Description This function is called to handle the read information
430 ** response.
431 **
432 **
433 ** Returns void
434 **
435 *******************************************************************************/
gatt_process_read_info_rsp(tGATT_TCB * p_tcb,tGATT_CLCB * p_clcb,UINT8 op_code,UINT16 len,UINT8 * p_data)436 void gatt_process_read_info_rsp(tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb, UINT8 op_code,
437 UINT16 len, UINT8 *p_data)
438 {
439 tGATT_DISC_RES result;
440 UINT8 *p = p_data, uuid_len = 0, type;
441
442 /* unexpected response */
443 if (p_clcb->operation != GATTC_OPTYPE_DISCOVERY || p_clcb->op_subtype != GATT_DISC_CHAR_DSCPT)
444 return;
445
446 STREAM_TO_UINT8(type, p);
447 len -= 1;
448
449 if (type == GATT_INFO_TYPE_PAIR_16)
450 uuid_len = LEN_UUID_16;
451 else if (type == GATT_INFO_TYPE_PAIR_128)
452 uuid_len = LEN_UUID_128;
453
454 while (len >= uuid_len + 2)
455 {
456 STREAM_TO_UINT16 (result.handle, p);
457
458 if (uuid_len > 0)
459 {
460 if (!gatt_parse_uuid_from_cmd(&result.type, uuid_len, &p))
461 break;
462 }
463 else
464 memcpy (&result.type, &p_clcb->uuid, sizeof(tBT_UUID));
465
466 len -= (uuid_len + 2);
467
468 if (p_clcb->p_reg->app_cb.p_disc_res_cb)
469 (*p_clcb->p_reg->app_cb.p_disc_res_cb)(p_clcb->conn_id, p_clcb->op_subtype, &result);
470 }
471
472 p_clcb->s_handle = (result.handle == 0) ? 0 :(result.handle + 1);
473 /* initiate another request */
474 gatt_act_discovery(p_clcb) ;
475 }
476 /*******************************************************************************
477 **
478 ** Function gatt_proc_disc_error_rsp
479 **
480 ** Description This function process the read by type response and send another
481 ** request if needed.
482 **
483 ** Returns void.
484 **
485 *******************************************************************************/
gatt_proc_disc_error_rsp(tGATT_TCB * p_tcb,tGATT_CLCB * p_clcb,UINT8 opcode,UINT16 handle,UINT8 reason)486 void gatt_proc_disc_error_rsp(tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb, UINT8 opcode,
487 UINT16 handle, UINT8 reason)
488 {
489 tGATT_STATUS status = (tGATT_STATUS) reason;
490
491 GATT_TRACE_DEBUG2("gatt_proc_disc_error_rsp reason: %02x cmd_code %04x", reason, opcode);
492
493 switch (opcode)
494 {
495 case GATT_REQ_READ_BY_GRP_TYPE:
496 case GATT_REQ_FIND_TYPE_VALUE:
497 case GATT_REQ_READ_BY_TYPE:
498 case GATT_REQ_FIND_INFO:
499 if (reason == GATT_NOT_FOUND)
500 {
501 status = GATT_SUCCESS;
502 GATT_TRACE_DEBUG0("Discovery completed");
503 }
504 break;
505 default:
506 GATT_TRACE_ERROR1("Incorrect discovery opcode %04x", opcode);
507 break;
508 }
509
510 gatt_end_operation(p_clcb, status, NULL);
511 }
512
513 /*******************************************************************************
514 **
515 ** Function gatt_process_error_rsp
516 **
517 ** Description This function is called to handle the error response
518 **
519 **
520 ** Returns void
521 **
522 *******************************************************************************/
gatt_process_error_rsp(tGATT_TCB * p_tcb,tGATT_CLCB * p_clcb,UINT8 op_code,UINT16 len,UINT8 * p_data)523 void gatt_process_error_rsp(tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb, UINT8 op_code,
524 UINT16 len, UINT8 *p_data)
525 {
526 UINT8 opcode, reason, * p= p_data;
527 UINT16 handle;
528 tGATT_VALUE *p_attr = (tGATT_VALUE *)p_clcb->p_attr_buf;
529
530 GATT_TRACE_DEBUG0("gatt_process_error_rsp ");
531 STREAM_TO_UINT8(opcode, p);
532 STREAM_TO_UINT16(handle, p);
533 STREAM_TO_UINT8(reason, p);
534
535 if (p_clcb->operation == GATTC_OPTYPE_DISCOVERY)
536 {
537 gatt_proc_disc_error_rsp(p_tcb, p_clcb, opcode, handle, reason);
538 }
539 else
540 {
541 if ( (p_clcb->operation == GATTC_OPTYPE_WRITE) &&
542 (p_clcb->op_subtype == GATT_WRITE) &&
543 (opcode == GATT_REQ_PREPARE_WRITE) &&
544 (p_attr) &&
545 (handle == p_attr->handle) )
546 {
547 p_clcb->status = reason;
548 gatt_send_queue_write_cancel(p_tcb, p_clcb, GATT_PREP_WRITE_CANCEL);
549 }
550 else if ((p_clcb->operation == GATTC_OPTYPE_READ) &&
551 ((p_clcb->op_subtype == GATT_READ_CHAR_VALUE_HDL) ||
552 (p_clcb->op_subtype == GATT_READ_BY_HANDLE)) &&
553 (opcode == GATT_REQ_READ_BLOB) &&
554 p_clcb->first_read_blob_after_read &&
555 (reason == GATT_NOT_LONG))
556 {
557 gatt_end_operation(p_clcb, GATT_SUCCESS, (void *)p_clcb->p_attr_buf);
558 }
559 else
560 gatt_end_operation(p_clcb, reason, NULL);
561 }
562 }
563 /*******************************************************************************
564 **
565 ** Function gatt_process_prep_write_rsp
566 **
567 ** Description This function is called to handle the read response
568 **
569 **
570 ** Returns void
571 **
572 *******************************************************************************/
gatt_process_prep_write_rsp(tGATT_TCB * p_tcb,tGATT_CLCB * p_clcb,UINT8 op_code,UINT16 len,UINT8 * p_data)573 void gatt_process_prep_write_rsp (tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb, UINT8 op_code,
574 UINT16 len, UINT8 *p_data)
575 {
576 tGATT_VALUE value = {0};
577 UINT8 *p= p_data;
578
579 GATT_TRACE_ERROR2("value resp op_code = %s len = %d", gatt_dbg_op_name(op_code), len);
580
581 STREAM_TO_UINT16 (value.handle, p);
582 STREAM_TO_UINT16 (value.offset, p);
583
584 value.len = len - 4;
585
586 memcpy (value.value, p, value.len);
587
588 if (p_clcb->op_subtype == GATT_WRITE_PREPARE)
589 {
590 p_clcb->status = GATT_SUCCESS;
591 /* application should verify handle offset
592 and value are matched or not */
593
594 gatt_end_operation(p_clcb, p_clcb->status, &value);
595 }
596 else if (p_clcb->op_subtype == GATT_WRITE )
597 {
598 if (!gatt_check_write_long_terminate(p_tcb, p_clcb, &value))
599 gatt_send_prepare_write(p_tcb, p_clcb);
600 }
601
602 }
603 /*******************************************************************************
604 **
605 ** Function gatt_process_notification
606 **
607 ** Description This function is called to handle the handle value indication
608 ** or handle value notification.
609 **
610 **
611 ** Returns void
612 **
613 *******************************************************************************/
gatt_process_notification(tGATT_TCB * p_tcb,UINT8 op_code,UINT16 len,UINT8 * p_data)614 void gatt_process_notification(tGATT_TCB *p_tcb, UINT8 op_code,
615 UINT16 len, UINT8 *p_data)
616 {
617 tGATT_VALUE value = {0};
618 tGATT_REG *p_reg;
619 UINT16 conn_id;
620 tGATT_STATUS encrypt_status;
621 UINT8 *p= p_data, i,
622 event = (op_code == GATT_HANDLE_VALUE_NOTIF) ? GATTC_OPTYPE_NOTIFICATION : GATTC_OPTYPE_INDICATION;
623
624 GATT_TRACE_DEBUG0("gatt_process_notification ");
625
626 STREAM_TO_UINT16 (value.handle, p);
627 value.len = len - 2;
628 memcpy (value.value, p, value.len);
629
630 if (!GATT_HANDLE_IS_VALID(value.handle))
631 {
632 /* illegal handle, send ack now */
633 if (op_code == GATT_HANDLE_VALUE_IND)
634 attp_send_cl_msg(p_tcb, 0, GATT_HANDLE_VALUE_CONF, NULL);
635 return;
636 }
637
638 if (event == GATTC_OPTYPE_INDICATION)
639 {
640 if (p_tcb->ind_count)
641 {
642 /* this is an error case that receiving an indication but we
643 still has an indication not being acked yet.
644 For now, just log the error reset the counter.
645 Later we need to disconnect the link unconditionally.
646 */
647 GATT_TRACE_ERROR1("gatt_process_notification rcv Ind. but ind_count=%d (will reset ind_count)", p_tcb->ind_count);
648 }
649 p_tcb->ind_count = 0;
650 }
651
652 /* should notify all registered client with the handle value notificaion/indication
653 Note: need to do the indication count and start timer first then do callback
654 */
655
656 for (i = 0, p_reg = gatt_cb.cl_rcb; i < GATT_MAX_APPS; i++, p_reg++)
657 {
658 if (p_reg->in_use && p_reg->app_cb.p_cmpl_cb && (event == GATTC_OPTYPE_INDICATION))
659 p_tcb->ind_count++;
660 }
661
662 if (event == GATTC_OPTYPE_INDICATION)
663 {
664 /* start a timer for app confirmation */
665 if (p_tcb->ind_count > 0)
666 gatt_start_ind_ack_timer(p_tcb);
667 else /* no app to indicate, or invalid handle */
668 attp_send_cl_msg(p_tcb, 0, GATT_HANDLE_VALUE_CONF, NULL);
669 }
670
671 encrypt_status = gatt_get_link_encrypt_status(p_tcb);
672 for (i = 0, p_reg = gatt_cb.cl_rcb; i < GATT_MAX_APPS; i++, p_reg++)
673 {
674 if (p_reg->in_use && p_reg->app_cb.p_cmpl_cb)
675 {
676 conn_id = GATT_CREATE_CONN_ID(p_tcb->tcb_idx, p_reg->gatt_if);
677 (*p_reg->app_cb.p_cmpl_cb) (conn_id, event, encrypt_status, (tGATT_CL_COMPLETE *)&value);
678 }
679 }
680
681 }
682
683 /*******************************************************************************
684 **
685 ** Function gatt_process_read_by_type_rsp
686 **
687 ** Description This function is called to handle the read by type response.
688 ** read by type can be used for discovery, or read by type or
689 ** read characteristic value.
690 **
691 ** Returns void
692 **
693 *******************************************************************************/
gatt_process_read_by_type_rsp(tGATT_TCB * p_tcb,tGATT_CLCB * p_clcb,UINT8 op_code,UINT16 len,UINT8 * p_data)694 void gatt_process_read_by_type_rsp (tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb, UINT8 op_code,
695 UINT16 len, UINT8 *p_data)
696 {
697 tGATT_DISC_RES result;
698 tGATT_DISC_VALUE record_value;
699 UINT8 *p = p_data, value_len, handle_len = 2;
700 UINT16 handle = 0;
701
702 /* discovery procedure and no callback function registered */
703 if (((!p_clcb->p_reg) || (!p_clcb->p_reg->app_cb.p_disc_res_cb)) && (p_clcb->operation == GATTC_OPTYPE_DISCOVERY))
704 return;
705
706 STREAM_TO_UINT8(value_len, p);
707
708 if ((value_len > (p_tcb->payload_size - 2)) || (value_len > (len-1)) )
709 {
710 /* this is an error case that server's response containing a value length which is larger than MTU-2
711 or value_len > message total length -1 */
712 GATT_TRACE_ERROR4("gatt_process_read_by_type_rsp: Discard response op_code=%d vale_len=%d > (MTU-2=%d or msg_len-1=%d)",
713 op_code, value_len, (p_tcb->payload_size - 2), (len-1));
714 gatt_end_operation(p_clcb, GATT_ERROR, NULL);
715 return;
716 }
717
718 if (op_code == GATT_RSP_READ_BY_GRP_TYPE)
719 handle_len = 4;
720
721 value_len -= handle_len; /* substract the handle pairs bytes */
722 len -= 1;
723
724 while (len >= (handle_len + value_len))
725 {
726 STREAM_TO_UINT16(handle, p);
727
728 if (!GATT_HANDLE_IS_VALID(handle))
729 {
730 gatt_end_operation(p_clcb, GATT_INVALID_HANDLE, NULL);
731 return;
732 }
733
734 memset(&result, 0, sizeof(tGATT_DISC_RES));
735 memset(&record_value, 0, sizeof(tGATT_DISC_VALUE));
736
737 result.handle = handle;
738 result.type.len = 2;
739 result.type.uu.uuid16 = disc_type_to_uuid[p_clcb->op_subtype];
740
741 /* discover all services */
742 if (p_clcb->operation == GATTC_OPTYPE_DISCOVERY &&
743 p_clcb->op_subtype == GATT_DISC_SRVC_ALL &&
744 op_code == GATT_RSP_READ_BY_GRP_TYPE)
745 {
746 STREAM_TO_UINT16(handle, p);
747
748 if (!GATT_HANDLE_IS_VALID(handle))
749 {
750 gatt_end_operation(p_clcb, GATT_INVALID_HANDLE, NULL);
751 return;
752 }
753 else
754 {
755 record_value.group_value.e_handle = handle;
756 if (!gatt_parse_uuid_from_cmd(&record_value.group_value.service_type, value_len, &p))
757 {
758 GATT_TRACE_ERROR0("discover all service response parsing failure");
759 break;
760 }
761 }
762 }
763 /* discover included service */
764 else if (p_clcb->operation == GATTC_OPTYPE_DISCOVERY && p_clcb->op_subtype == GATT_DISC_INC_SRVC)
765 {
766 STREAM_TO_UINT16(record_value.incl_service.s_handle, p);
767 STREAM_TO_UINT16(record_value.incl_service.e_handle, p);
768
769 if (!GATT_HANDLE_IS_VALID(record_value.incl_service.s_handle) ||
770 !GATT_HANDLE_IS_VALID(record_value.incl_service.e_handle))
771 {
772 gatt_end_operation(p_clcb, GATT_INVALID_HANDLE, NULL);
773 return;
774 }
775
776 if(value_len == 6)
777 {
778 STREAM_TO_UINT16(record_value.incl_service.service_type.uu.uuid16, p);
779 record_value.incl_service.service_type.len = LEN_UUID_16;
780 }
781 else if (value_len == 4)
782 {
783 p_clcb->s_handle = record_value.incl_service.s_handle;
784 p_clcb->read_uuid128.wait_for_read_rsp = TRUE;
785 p_clcb->read_uuid128.next_disc_start_hdl = handle + 1;
786 memcpy(&p_clcb->read_uuid128.result, &result, sizeof(result));
787 memcpy(&p_clcb->read_uuid128.result.value, &record_value, sizeof (result.value));
788 p_clcb->op_subtype |= 0x90;
789 gatt_act_read(p_clcb, 0);
790 return;
791 }
792 else
793 {
794 GATT_TRACE_ERROR1("gatt_process_read_by_type_rsp INCL_SRVC failed with invalid data value_len=%d", value_len);
795 gatt_end_operation(p_clcb, GATT_INVALID_PDU, (void *)p);
796 return;
797 }
798 }
799 /* read by type */
800 else if (p_clcb->operation == GATTC_OPTYPE_READ && p_clcb->op_subtype == GATT_READ_BY_TYPE)
801 {
802 p_clcb->counter = len - 2;
803 p_clcb->s_handle = handle;
804 if ( p_clcb->counter == (p_clcb->p_tcb->payload_size -4))
805 {
806 p_clcb->op_subtype = GATT_READ_BY_HANDLE;
807 if (!p_clcb->p_attr_buf)
808 p_clcb->p_attr_buf = (UINT8 *)GKI_getbuf(GATT_MAX_ATTR_LEN);
809 if (p_clcb->p_attr_buf && p_clcb->counter <= GATT_MAX_ATTR_LEN)
810 {
811 memcpy(p_clcb->p_attr_buf, p, p_clcb->counter);
812 gatt_act_read(p_clcb, p_clcb->counter);
813 }
814 else
815 gatt_end_operation(p_clcb, GATT_INTERNAL_ERROR, (void *)p);
816 }
817 else
818 {
819 gatt_end_operation(p_clcb, GATT_SUCCESS, (void *)p);
820 }
821 return;
822 }
823 else /* discover characterisitic or read characteristic value */
824 {
825 STREAM_TO_UINT8 (record_value.dclr_value.char_prop, p);
826 STREAM_TO_UINT16(record_value.dclr_value.val_handle, p);
827 if (!GATT_HANDLE_IS_VALID(record_value.dclr_value.val_handle))
828 {
829 gatt_end_operation(p_clcb, GATT_INVALID_HANDLE, NULL);
830 return;
831 }
832 if (!gatt_parse_uuid_from_cmd(&record_value.dclr_value.char_uuid, (UINT16)(value_len - 3), &p))
833 {
834 gatt_end_operation(p_clcb, GATT_SUCCESS, NULL);
835 /* invalid format, and skip the result */
836 return;
837 }
838
839 /* UUID not matching */
840 if (!gatt_uuid_compare(record_value.dclr_value.char_uuid, p_clcb->uuid))
841 {
842 len -= (value_len + 2);
843 continue; /* skip the result, and look for next one */
844 }
845 else if (p_clcb->operation == GATTC_OPTYPE_READ)
846 /* UUID match for read characteristic value */
847 {
848 /* only read the first matching UUID characteristic value, and
849 discard the rest results */
850 p_clcb->s_handle = record_value.dclr_value.val_handle;
851 p_clcb->op_subtype |= 0x80;
852 gatt_act_read(p_clcb, 0);
853 return;
854 }
855 }
856 len -= (value_len + handle_len);
857
858 /* result is (handle, 16bits UUID) pairs */
859 memcpy (&result.value, &record_value, sizeof (result.value));
860
861 /* send callback if is discover procedure */
862 if (p_clcb->operation == GATTC_OPTYPE_DISCOVERY && p_clcb->p_reg->app_cb.p_disc_res_cb)
863 (*p_clcb->p_reg->app_cb.p_disc_res_cb)(p_clcb->conn_id, p_clcb->op_subtype, &result);
864 }
865
866 p_clcb->s_handle = (handle == 0) ? 0 : (handle + 1);
867
868 if (p_clcb->operation == GATTC_OPTYPE_DISCOVERY)
869 {
870 /* initiate another request */
871 gatt_act_discovery(p_clcb) ;
872 }
873 else /* read characteristic value */
874 {
875 gatt_act_read(p_clcb, 0);
876 }
877 }
878
879 /*******************************************************************************
880 **
881 ** Function gatt_process_read_rsp
882 **
883 ** Description This function is called to handle the read BLOB response
884 **
885 **
886 ** Returns void
887 **
888 *******************************************************************************/
gatt_process_read_rsp(tGATT_TCB * p_tcb,tGATT_CLCB * p_clcb,UINT8 op_code,UINT16 len,UINT8 * p_data)889 void gatt_process_read_rsp(tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb, UINT8 op_code,
890 UINT16 len, UINT8 *p_data)
891 {
892 UINT16 offset = p_clcb->counter;
893 UINT8 * p= p_data;
894
895 if (p_clcb->operation == GATTC_OPTYPE_READ)
896 {
897 if (p_clcb->op_subtype != GATT_READ_BY_HANDLE)
898 {
899 p_clcb->counter = len;
900 gatt_end_operation(p_clcb, GATT_SUCCESS, (void *)p);
901 }
902 else
903 {
904
905 /* allocate GKI buffer holding up long attribute value */
906 if (!p_clcb->p_attr_buf)
907 p_clcb->p_attr_buf = (UINT8 *)GKI_getbuf(GATT_MAX_ATTR_LEN);
908
909 /* copy attrobute value into cb buffer */
910 if (p_clcb->p_attr_buf && offset < GATT_MAX_ATTR_LEN)
911 {
912 if ((len + offset) > GATT_MAX_ATTR_LEN)
913 len = GATT_MAX_ATTR_LEN - offset;
914
915 p_clcb->counter += len;
916
917 memcpy(p_clcb->p_attr_buf + offset, p, len);
918
919 /* send next request if needed */
920
921 if (len == (p_tcb->payload_size - 1) && /* full packet for read or read blob rsp */
922 len + offset < GATT_MAX_ATTR_LEN)
923 {
924 GATT_TRACE_DEBUG3("full pkt issue read blob for remianing bytes old offset=%d len=%d new offset=%d",
925 offset, len, p_clcb->counter);
926 gatt_act_read(p_clcb, p_clcb->counter);
927 }
928 else /* end of request, send callback */
929 {
930 gatt_end_operation(p_clcb, GATT_SUCCESS, (void *)p_clcb->p_attr_buf);
931 }
932 }
933 else /* exception, should not happen */
934 {
935 GATT_TRACE_ERROR2("attr offset = %d p_attr_buf = %d ", offset, p_clcb->p_attr_buf);
936 gatt_end_operation(p_clcb, GATT_NO_RESOURCES, (void *)p_clcb->p_attr_buf);
937 }
938 }
939 }
940 else
941 {
942 if (p_clcb->operation == GATTC_OPTYPE_DISCOVERY &&
943 p_clcb->op_subtype == GATT_DISC_INC_SRVC &&
944 p_clcb->read_uuid128.wait_for_read_rsp )
945 {
946 p_clcb->s_handle = p_clcb->read_uuid128.next_disc_start_hdl;
947 p_clcb->read_uuid128.wait_for_read_rsp = FALSE;
948 if (len == LEN_UUID_128)
949 {
950
951 memcpy(p_clcb->read_uuid128.result.value.incl_service.service_type.uu.uuid128, p, len);
952 p_clcb->read_uuid128.result.value.incl_service.service_type.len = LEN_UUID_128;
953 if ( p_clcb->p_reg->app_cb.p_disc_res_cb)
954 (*p_clcb->p_reg->app_cb.p_disc_res_cb)(p_clcb->conn_id, p_clcb->op_subtype, &p_clcb->read_uuid128.result);
955 gatt_act_discovery(p_clcb) ;
956 }
957 else
958 {
959 gatt_end_operation(p_clcb, GATT_INVALID_PDU, (void *)p);
960 }
961 }
962 }
963
964 }
965
966
967 /*******************************************************************************
968 **
969 ** Function gatt_process_handle_rsp
970 **
971 ** Description This function is called to handle the write response
972 **
973 **
974 ** Returns void
975 **
976 *******************************************************************************/
gatt_process_handle_rsp(tGATT_TCB * p_tcb,tGATT_CLCB * p_clcb,UINT8 op_code,UINT16 len,UINT8 * p_data)977 void gatt_process_handle_rsp(tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb, UINT8 op_code, UINT16 len, UINT8 *p_data)
978 {
979 UINT16 handle;
980 UINT8 * p= p_data;
981
982 STREAM_TO_UINT16(handle, p);
983 len -= 2;
984
985 if (op_code == GATT_RSP_WRITE)
986 gatt_end_operation(p_clcb, GATT_SUCCESS, NULL);
987 }
988 /*******************************************************************************
989 **
990 ** Function gatt_process_mtu_rsp
991 **
992 ** Description This function is called to process the configure MTU response.
993 **
994 **
995 ** Returns void
996 **
997 *******************************************************************************/
gatt_process_mtu_rsp(tGATT_TCB * p_tcb,tGATT_CLCB * p_clcb,UINT16 len,UINT8 * p_data)998 void gatt_process_mtu_rsp(tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb, UINT16 len, UINT8 *p_data)
999 {
1000 UINT16 mtu;
1001
1002 STREAM_TO_UINT16(mtu, p_data);
1003
1004 if (mtu < p_tcb->payload_size && mtu >= GATT_DEF_BLE_MTU_SIZE)
1005 p_tcb->payload_size = mtu;
1006
1007 gatt_end_operation(p_clcb, p_clcb->status, NULL);
1008 }
1009 /*******************************************************************************
1010 **
1011 ** Function gatt_cmd_to_rsp_code
1012 **
1013 ** Description The function convert a ATT command op code into the corresponding
1014 ** response code assume no error occurs.
1015 **
1016 ** Returns response code.
1017 **
1018 *******************************************************************************/
gatt_cmd_to_rsp_code(UINT8 cmd_code)1019 UINT8 gatt_cmd_to_rsp_code (UINT8 cmd_code)
1020 {
1021 UINT8 rsp_code = 0;
1022
1023 if (cmd_code > 1 && cmd_code != GATT_CMD_WRITE)
1024 {
1025 rsp_code = cmd_code + 1;
1026 }
1027 return rsp_code;
1028 }
1029 /*******************************************************************************
1030 **
1031 ** Function gatt_cl_send_next_cmd_inq
1032 **
1033 ** Description Find next command in queue and sent to server
1034 **
1035 ** Returns TRUE if command sent, otherwise FALSE.
1036 **
1037 *******************************************************************************/
gatt_cl_send_next_cmd_inq(tGATT_TCB * p_tcb)1038 BOOLEAN gatt_cl_send_next_cmd_inq(tGATT_TCB *p_tcb)
1039 {
1040 tGATT_CMD_Q *p_cmd = &p_tcb->cl_cmd_q[p_tcb->pending_cl_req];
1041 BOOLEAN sent = FALSE;
1042 UINT8 rsp_code;
1043 tGATT_CLCB *p_clcb = NULL;
1044
1045 while (!sent &&
1046 p_tcb->pending_cl_req != p_tcb->next_slot_inq &&
1047 p_cmd->to_send && p_cmd->p_cmd != NULL)
1048 {
1049 sent = attp_send_msg_to_L2CAP(p_tcb, p_cmd->p_cmd);
1050
1051 if (sent)
1052 {
1053 p_cmd->to_send = FALSE;
1054 p_cmd->p_cmd = NULL;
1055
1056 /* dequeue the request if is write command or sign write */
1057 if (p_cmd->op_code != GATT_CMD_WRITE && p_cmd->op_code != GATT_SIGN_CMD_WRITE)
1058 {
1059 gatt_start_rsp_timer (p_tcb);
1060 }
1061 else
1062 {
1063 p_clcb = gatt_cmd_dequeue(p_tcb, &rsp_code);
1064
1065 /* if no ack needed, keep sending */
1066 sent = FALSE;
1067 p_cmd = &p_tcb->cl_cmd_q[p_tcb->pending_cl_req];
1068 /* send command complete callback here */
1069 gatt_end_operation(p_clcb, GATT_SUCCESS, NULL);
1070 }
1071 }
1072 else
1073 {
1074 GATT_TRACE_ERROR0("gatt_cl_send_next_cmd_inq: L2CAP sent error");
1075
1076 memset(p_cmd, 0, sizeof(tGATT_CMD_Q));
1077 p_tcb->pending_cl_req ++;
1078 p_cmd = &p_tcb->cl_cmd_q[p_tcb->pending_cl_req];
1079 }
1080
1081 }
1082 return sent;
1083 }
1084
1085 /*******************************************************************************
1086 **
1087 ** Function gatt_client_handle_server_rsp
1088 **
1089 ** Description This function is called to handle the server response to
1090 ** client.
1091 **
1092 **
1093 ** Returns void
1094 **
1095 *******************************************************************************/
gatt_client_handle_server_rsp(tGATT_TCB * p_tcb,UINT8 op_code,UINT16 len,UINT8 * p_data)1096 void gatt_client_handle_server_rsp (tGATT_TCB *p_tcb, UINT8 op_code,
1097 UINT16 len, UINT8 *p_data)
1098 {
1099 tGATT_CLCB *p_clcb = NULL;
1100 UINT8 rsp_code;
1101
1102 if (op_code != GATT_HANDLE_VALUE_IND && op_code != GATT_HANDLE_VALUE_NOTIF)
1103 {
1104 p_clcb = gatt_cmd_dequeue(p_tcb, &rsp_code);
1105
1106 rsp_code = gatt_cmd_to_rsp_code(rsp_code);
1107
1108 if (p_clcb == NULL || (rsp_code != op_code && op_code != GATT_RSP_ERROR))
1109 {
1110 GATT_TRACE_WARNING2 ("ATT - Ignore wrong response. Receives (%02x) \
1111 Request(%02x) Ignored", op_code, rsp_code);
1112
1113 return;
1114 }
1115 else
1116 btu_stop_timer (&p_tcb->rsp_timer_ent);
1117 }
1118 /* the size of the message may not be bigger than the local max PDU size*/
1119 /* The message has to be smaller than the agreed MTU, len does not count op_code */
1120 if (len >= p_tcb->payload_size)
1121 {
1122 GATT_TRACE_ERROR2("invalid response/indicate pkt size: %d, PDU size: %d", len + 1, p_tcb->payload_size);
1123 if (op_code != GATT_HANDLE_VALUE_NOTIF &&
1124 op_code != GATT_HANDLE_VALUE_IND)
1125 gatt_end_operation(p_clcb, GATT_ERROR, NULL);
1126 }
1127 else
1128 {
1129 switch (op_code)
1130 {
1131 case GATT_RSP_ERROR:
1132 gatt_process_error_rsp(p_tcb, p_clcb, op_code, len, p_data);
1133 break;
1134
1135 case GATT_RSP_MTU: /* 2 bytes mtu */
1136 gatt_process_mtu_rsp(p_tcb, p_clcb, len ,p_data);
1137 break;
1138
1139 case GATT_RSP_FIND_INFO:
1140 gatt_process_read_info_rsp(p_tcb, p_clcb, op_code, len, p_data);
1141 break;
1142
1143 case GATT_RSP_READ_BY_TYPE:
1144 case GATT_RSP_READ_BY_GRP_TYPE:
1145 gatt_process_read_by_type_rsp(p_tcb, p_clcb, op_code, len, p_data);
1146 break;
1147
1148 case GATT_RSP_READ:
1149 case GATT_RSP_READ_BLOB:
1150 case GATT_RSP_READ_MULTI:
1151 gatt_process_read_rsp(p_tcb, p_clcb, op_code, len, p_data);
1152 break;
1153
1154 case GATT_RSP_FIND_TYPE_VALUE: /* disc service with UUID */
1155 gatt_process_find_type_value_rsp(p_tcb, p_clcb, len, p_data);
1156 break;
1157
1158 case GATT_RSP_WRITE:
1159 gatt_process_handle_rsp(p_tcb, p_clcb, op_code, len, p_data);
1160 break;
1161
1162 case GATT_RSP_PREPARE_WRITE:
1163 gatt_process_prep_write_rsp(p_tcb, p_clcb, op_code, len, p_data);
1164 break;
1165
1166 case GATT_RSP_EXEC_WRITE:
1167 gatt_end_operation(p_clcb, p_clcb->status, NULL);
1168 break;
1169
1170 case GATT_HANDLE_VALUE_NOTIF:
1171 case GATT_HANDLE_VALUE_IND:
1172 gatt_process_notification(p_tcb, op_code, len, p_data);
1173 break;
1174
1175 default:
1176 GATT_TRACE_ERROR1("Unknown opcode = %d", op_code);
1177 break;
1178 }
1179 }
1180
1181 if (op_code != GATT_HANDLE_VALUE_IND && op_code != GATT_HANDLE_VALUE_NOTIF)
1182 {
1183 gatt_cl_send_next_cmd_inq(p_tcb);
1184 }
1185
1186 return;
1187 }
1188
1189 #endif /* BLE_INCLUDED */
1190