1 /****************************************************************************** 2 * 3 * Copyright (C) 2009-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 20 /************************************************************************************ 21 * 22 * Filename: btif_gatt_server.c 23 * 24 * Description: GATT server implementation 25 * 26 ***********************************************************************************/ 27 28 #include <hardware/bluetooth.h> 29 #include <hardware/bt_gatt.h> 30 #include <stdio.h> 31 #include <stdlib.h> 32 #include <errno.h> 33 #include <string.h> 34 35 #define LOG_TAG "BtGatt.btif" 36 37 #include "btif_common.h" 38 #include "btif_util.h" 39 40 #if (defined(BLE_INCLUDED) && (BLE_INCLUDED == TRUE)) 41 42 #include "gki.h" 43 #include "bta_api.h" 44 #include "bta_gatt_api.h" 45 #include "bd.h" 46 #include "btif_dm.h" 47 #include "btif_storage.h" 48 49 #include "btif_gatt.h" 50 #include "btif_gatt_util.h" 51 52 /************************************************************************************ 53 ** Constants & Macros 54 ************************************************************************************/ 55 56 #define CHECK_BTGATT_INIT() if (bt_gatt_callbacks == NULL)\ 57 {\ 58 ALOGW("%s: BTGATT not initialized", __FUNCTION__);\ 59 return BT_STATUS_NOT_READY;\ 60 } else {\ 61 ALOGD("%s", __FUNCTION__);\ 62 } 63 64 65 typedef enum { 66 BTIF_GATTS_REGISTER_APP = 2000, 67 BTIF_GATTS_UNREGISTER_APP, 68 BTIF_GATTS_OPEN, 69 BTIF_GATTS_CLOSE, 70 BTIF_GATTS_CREATE_SERVICE, 71 BTIF_GATTS_ADD_INCLUDED_SERVICE, 72 BTIF_GATTS_ADD_CHARACTERISTIC, 73 BTIF_GATTS_ADD_DESCRIPTOR, 74 BTIF_GATTS_START_SERVICE, 75 BTIF_GATTS_STOP_SERVICE, 76 BTIF_GATTS_DELETE_SERVICE, 77 BTIF_GATTS_SEND_INDICATION, 78 BTIF_GATTS_SEND_RESPONSE 79 } btif_gatts_event_t; 80 81 /************************************************************************************ 82 ** Local type definitions 83 ************************************************************************************/ 84 85 typedef struct 86 { 87 uint8_t value[BTGATT_MAX_ATTR_LEN]; 88 btgatt_response_t response; 89 btgatt_srvc_id_t srvc_id; 90 bt_bdaddr_t bd_addr; 91 bt_uuid_t uuid; 92 uint32_t trans_id; 93 uint16_t conn_id; 94 uint16_t srvc_handle; 95 uint16_t incl_handle; 96 uint16_t attr_handle; 97 uint16_t permissions; 98 uint16_t len; 99 uint8_t server_if; 100 uint8_t is_direct; 101 uint8_t num_handles; 102 uint8_t properties; 103 uint8_t transport; 104 uint8_t confirm; 105 uint8_t status; 106 } __attribute__((packed)) btif_gatts_cb_t; 107 108 109 /************************************************************************************ 110 ** Static variables 111 ************************************************************************************/ 112 113 extern const btgatt_callbacks_t *bt_gatt_callbacks; 114 115 116 /************************************************************************************ 117 ** Static functions 118 ************************************************************************************/ 119 120 static void btapp_gatts_copy_req_data(UINT16 event, char *p_dest, char *p_src) 121 { 122 tBTA_GATTS *p_dest_data = (tBTA_GATTS*) p_dest; 123 tBTA_GATTS *p_src_data = (tBTA_GATTS*) p_src; 124 125 if (!p_src_data || !p_dest_data) 126 return; 127 128 // Copy basic structure first 129 memcpy(p_dest_data, p_src_data, sizeof(tBTA_GATTS)); 130 131 // Allocate buffer for request data if necessary 132 switch (event) 133 { 134 case BTA_GATTS_READ_EVT: 135 case BTA_GATTS_WRITE_EVT: 136 case BTA_GATTS_EXEC_WRITE_EVT: 137 case BTA_GATTS_MTU_EVT: 138 p_dest_data->req_data.p_data = GKI_getbuf(sizeof(tBTA_GATTS_REQ_DATA)); 139 if (p_dest_data->req_data.p_data != NULL) 140 { 141 memcpy(p_dest_data->req_data.p_data, p_src_data->req_data.p_data, 142 sizeof(tBTA_GATTS_REQ_DATA)); 143 } 144 break; 145 146 default: 147 break; 148 } 149 } 150 151 static void btapp_gatts_free_req_data(UINT16 event, tBTA_GATTS *p_data) 152 { 153 switch (event) 154 { 155 case BTA_GATTS_READ_EVT: 156 case BTA_GATTS_WRITE_EVT: 157 case BTA_GATTS_EXEC_WRITE_EVT: 158 case BTA_GATTS_MTU_EVT: 159 if (p_data && p_data->req_data.p_data) 160 GKI_freebuf(p_data->req_data.p_data); 161 break; 162 163 default: 164 break; 165 } 166 } 167 168 static void btapp_gatts_handle_cback(uint16_t event, char* p_param) 169 { 170 ALOGD("%s: Event %d", __FUNCTION__, event); 171 172 tBTA_GATTS *p_data = (tBTA_GATTS*)p_param; 173 switch (event) 174 { 175 case BTA_GATTS_REG_EVT: 176 { 177 bt_uuid_t app_uuid; 178 bta_to_btif_uuid(&app_uuid, &p_data->reg_oper.uuid); 179 HAL_CBACK(bt_gatt_callbacks, server->register_server_cb 180 , p_data->reg_oper.status 181 , p_data->reg_oper.server_if 182 , &app_uuid 183 ); 184 break; 185 } 186 187 case BTA_GATTS_DEREG_EVT: 188 break; 189 190 case BTA_GATTS_CONNECT_EVT: 191 { 192 bt_bdaddr_t bda; 193 bdcpy(bda.address, p_data->conn.remote_bda); 194 195 btif_gatt_check_encrypted_link(p_data->conn.remote_bda); 196 197 HAL_CBACK(bt_gatt_callbacks, server->connection_cb, 198 p_data->conn.conn_id, p_data->conn.server_if, TRUE, &bda); 199 break; 200 } 201 202 case BTA_GATTS_DISCONNECT_EVT: 203 { 204 bt_bdaddr_t bda; 205 bdcpy(bda.address, p_data->conn.remote_bda); 206 207 HAL_CBACK(bt_gatt_callbacks, server->connection_cb, 208 p_data->conn.conn_id, p_data->conn.server_if, FALSE, &bda); 209 break; 210 } 211 212 case BTA_GATTS_CREATE_EVT: 213 { 214 btgatt_srvc_id_t srvc_id; 215 srvc_id.is_primary = p_data->create.is_primary; 216 srvc_id.id.inst_id = p_data->create.svc_instance; 217 bta_to_btif_uuid(&srvc_id.id.uuid, &p_data->create.uuid); 218 219 HAL_CBACK(bt_gatt_callbacks, server->service_added_cb, 220 p_data->create.status, p_data->create.server_if, &srvc_id, 221 p_data->create.service_id 222 ); 223 } 224 break; 225 226 case BTA_GATTS_ADD_INCL_SRVC_EVT: 227 HAL_CBACK(bt_gatt_callbacks, server->included_service_added_cb, 228 p_data->add_result.status, 229 p_data->add_result.server_if, 230 p_data->add_result.service_id, 231 p_data->add_result.attr_id); 232 break; 233 234 case BTA_GATTS_ADD_CHAR_EVT: 235 { 236 bt_uuid_t uuid; 237 bta_to_btif_uuid(&uuid, &p_data->add_result.char_uuid); 238 239 HAL_CBACK(bt_gatt_callbacks, server->characteristic_added_cb, 240 p_data->add_result.status, 241 p_data->add_result.server_if, 242 &uuid, 243 p_data->add_result.service_id, 244 p_data->add_result.attr_id); 245 break; 246 } 247 248 case BTA_GATTS_ADD_CHAR_DESCR_EVT: 249 { 250 bt_uuid_t uuid; 251 bta_to_btif_uuid(&uuid, &p_data->add_result.char_uuid); 252 253 HAL_CBACK(bt_gatt_callbacks, server->descriptor_added_cb, 254 p_data->add_result.status, 255 p_data->add_result.server_if, 256 &uuid, 257 p_data->add_result.service_id, 258 p_data->add_result.attr_id); 259 break; 260 } 261 262 case BTA_GATTS_START_EVT: 263 HAL_CBACK(bt_gatt_callbacks, server->service_started_cb, 264 p_data->srvc_oper.status, 265 p_data->srvc_oper.server_if, 266 p_data->srvc_oper.service_id); 267 break; 268 269 case BTA_GATTS_STOP_EVT: 270 HAL_CBACK(bt_gatt_callbacks, server->service_stopped_cb, 271 p_data->srvc_oper.status, 272 p_data->srvc_oper.server_if, 273 p_data->srvc_oper.service_id); 274 break; 275 276 case BTA_GATTS_DELELTE_EVT: 277 HAL_CBACK(bt_gatt_callbacks, server->service_deleted_cb, 278 p_data->srvc_oper.status, 279 p_data->srvc_oper.server_if, 280 p_data->srvc_oper.service_id); 281 break; 282 283 case BTA_GATTS_READ_EVT: 284 { 285 bt_bdaddr_t bda; 286 bdcpy(bda.address, p_data->req_data.remote_bda); 287 288 HAL_CBACK(bt_gatt_callbacks, server->request_read_cb, 289 p_data->req_data.conn_id,p_data->req_data.trans_id, &bda, 290 p_data->req_data.p_data->read_req.handle, 291 p_data->req_data.p_data->read_req.offset, 292 p_data->req_data.p_data->read_req.is_long); 293 break; 294 } 295 296 case BTA_GATTS_WRITE_EVT: 297 { 298 bt_bdaddr_t bda; 299 bdcpy(bda.address, p_data->req_data.remote_bda); 300 301 HAL_CBACK(bt_gatt_callbacks, server->request_write_cb, 302 p_data->req_data.conn_id,p_data->req_data.trans_id, &bda, 303 p_data->req_data.p_data->write_req.handle, 304 p_data->req_data.p_data->write_req.offset, 305 p_data->req_data.p_data->write_req.len, 306 p_data->req_data.p_data->write_req.need_rsp, 307 p_data->req_data.p_data->write_req.is_prep, 308 p_data->req_data.p_data->write_req.value); 309 break; 310 } 311 312 case BTA_GATTS_EXEC_WRITE_EVT: 313 { 314 bt_bdaddr_t bda; 315 bdcpy(bda.address, p_data->req_data.remote_bda); 316 317 HAL_CBACK(bt_gatt_callbacks, server->request_exec_write_cb, 318 p_data->req_data.conn_id,p_data->req_data.trans_id, &bda, 319 p_data->req_data.p_data->exec_write); 320 break; 321 } 322 323 case BTA_GATTS_MTU_EVT: 324 case BTA_GATTS_OPEN_EVT: 325 case BTA_GATTS_CANCEL_OPEN_EVT: 326 case BTA_GATTS_CLOSE_EVT: 327 ALOGD("%s: Empty event (%d)!", __FUNCTION__, event); 328 break; 329 330 default: 331 ALOGE("%s: Unhandled event (%d)!", __FUNCTION__, event); 332 break; 333 } 334 335 btapp_gatts_free_req_data(event, p_data); 336 } 337 338 static void btapp_gatts_cback(tBTA_GATTS_EVT event, tBTA_GATTS *p_data) 339 { 340 bt_status_t status; 341 status = btif_transfer_context(btapp_gatts_handle_cback, (uint16_t) event, 342 (void*)p_data, sizeof(tBTA_GATTS), btapp_gatts_copy_req_data); 343 ASSERTC(status == BT_STATUS_SUCCESS, "Context transfer failed!", status); 344 } 345 346 static void btgatts_handle_event(uint16_t event, char* p_param) 347 { 348 btif_gatts_cb_t* p_cb = (btif_gatts_cb_t*)p_param; 349 if (!p_cb) return; 350 351 ALOGD("%s: Event %d", __FUNCTION__, event); 352 353 switch (event) 354 { 355 case BTIF_GATTS_REGISTER_APP: 356 { 357 tBT_UUID uuid; 358 btif_to_bta_uuid(&uuid, &p_cb->uuid); 359 BTA_GATTS_AppRegister(&uuid, btapp_gatts_cback); 360 break; 361 } 362 363 case BTIF_GATTS_UNREGISTER_APP: 364 BTA_GATTS_AppDeregister(p_cb->server_if); 365 break; 366 367 case BTIF_GATTS_OPEN: 368 if (!p_cb->is_direct) 369 BTA_DmBleSetBgConnType(BTM_BLE_CONN_AUTO, NULL); 370 BTA_GATTS_Open(p_cb->server_if, p_cb->bd_addr.address, 371 p_cb->is_direct); 372 break; 373 374 case BTIF_GATTS_CLOSE: 375 // Cancel pending foreground/background connections 376 BTA_GATTS_CancelOpen(p_cb->server_if, p_cb->bd_addr.address, TRUE); 377 BTA_GATTS_CancelOpen(p_cb->server_if, p_cb->bd_addr.address, FALSE); 378 379 // Close active connection 380 if (p_cb->conn_id != 0) 381 BTA_GATTS_Close(p_cb->conn_id); 382 break; 383 384 case BTIF_GATTS_CREATE_SERVICE: 385 { 386 tBTA_GATT_SRVC_ID srvc_id; 387 btif_to_bta_srvc_id(&srvc_id, &p_cb->srvc_id); 388 BTA_GATTS_CreateService(p_cb->server_if, &srvc_id.id.uuid, 389 srvc_id.id.inst_id, p_cb->num_handles, 390 srvc_id.is_primary); 391 break; 392 } 393 394 case BTIF_GATTS_ADD_INCLUDED_SERVICE: 395 BTA_GATTS_AddIncludeService(p_cb->srvc_handle, p_cb->incl_handle); 396 break; 397 398 case BTIF_GATTS_ADD_CHARACTERISTIC: 399 { 400 tBT_UUID uuid; 401 btif_to_bta_uuid(&uuid, &p_cb->uuid); 402 403 BTA_GATTS_AddCharacteristic(p_cb->srvc_handle, &uuid, 404 p_cb->permissions, p_cb->properties); 405 break; 406 } 407 408 case BTIF_GATTS_ADD_DESCRIPTOR: 409 { 410 tBT_UUID uuid; 411 btif_to_bta_uuid(&uuid, &p_cb->uuid); 412 413 BTA_GATTS_AddCharDescriptor(p_cb->srvc_handle, p_cb->permissions, 414 &uuid); 415 break; 416 } 417 418 case BTIF_GATTS_START_SERVICE: 419 BTA_GATTS_StartService(p_cb->srvc_handle, p_cb->transport); 420 break; 421 422 case BTIF_GATTS_STOP_SERVICE: 423 BTA_GATTS_StopService(p_cb->srvc_handle); 424 break; 425 426 case BTIF_GATTS_DELETE_SERVICE: 427 BTA_GATTS_DeleteService(p_cb->srvc_handle); 428 break; 429 430 case BTIF_GATTS_SEND_INDICATION: 431 BTA_GATTS_HandleValueIndication(p_cb->conn_id, p_cb->attr_handle, 432 p_cb->len, p_cb->value, p_cb->confirm); 433 // TODO: Might need to send an ACK if handle value indication is 434 // invoked without need for confirmation. 435 break; 436 437 case BTIF_GATTS_SEND_RESPONSE: 438 { 439 tBTA_GATTS_RSP rsp_struct; 440 btgatt_response_t *p_rsp = &p_cb->response; 441 btif_to_bta_response(&rsp_struct, p_rsp); 442 443 BTA_GATTS_SendRsp(p_cb->conn_id, p_cb->trans_id, 444 p_cb->status, &rsp_struct); 445 446 HAL_CBACK(bt_gatt_callbacks, server->response_confirmation_cb, 447 0, rsp_struct.attr_value.handle); 448 break; 449 } 450 451 default: 452 ALOGE("%s: Unknown event (%d)!", __FUNCTION__, event); 453 break; 454 } 455 } 456 457 /************************************************************************************ 458 ** Server API Functions 459 ************************************************************************************/ 460 461 static bt_status_t btif_gatts_register_app(bt_uuid_t *uuid) 462 { 463 CHECK_BTGATT_INIT(); 464 btif_gatts_cb_t btif_cb; 465 memcpy(&btif_cb.uuid, uuid, sizeof(bt_uuid_t)); 466 return btif_transfer_context(btgatts_handle_event, BTIF_GATTS_REGISTER_APP, 467 (char*) &btif_cb, sizeof(btif_gatts_cb_t), NULL); 468 } 469 470 static bt_status_t btif_gatts_unregister_app( int server_if ) 471 { 472 CHECK_BTGATT_INIT(); 473 btif_gatts_cb_t btif_cb; 474 btif_cb.server_if = (uint8_t) server_if; 475 return btif_transfer_context(btgatts_handle_event, BTIF_GATTS_UNREGISTER_APP, 476 (char*) &btif_cb, sizeof(btif_gatts_cb_t), NULL); 477 } 478 479 static bt_status_t btif_gatts_open( int server_if, const bt_bdaddr_t *bd_addr, bool is_direct ) 480 { 481 CHECK_BTGATT_INIT(); 482 btif_gatts_cb_t btif_cb; 483 btif_cb.server_if = (uint8_t) server_if; 484 btif_cb.is_direct = is_direct ? 1 : 0; 485 bdcpy(btif_cb.bd_addr.address, bd_addr->address); 486 return btif_transfer_context(btgatts_handle_event, BTIF_GATTS_OPEN, 487 (char*) &btif_cb, sizeof(btif_gatts_cb_t), NULL); 488 } 489 490 static bt_status_t btif_gatts_close(int server_if, const bt_bdaddr_t *bd_addr, int conn_id) 491 { 492 CHECK_BTGATT_INIT(); 493 btif_gatts_cb_t btif_cb; 494 btif_cb.server_if = (uint8_t) server_if; 495 btif_cb.conn_id = (uint16_t) conn_id; 496 bdcpy(btif_cb.bd_addr.address, bd_addr->address); 497 return btif_transfer_context(btgatts_handle_event, BTIF_GATTS_CLOSE, 498 (char*) &btif_cb, sizeof(btif_gatts_cb_t), NULL); 499 } 500 501 static bt_status_t btif_gatts_add_service(int server_if, btgatt_srvc_id_t *srvc_id, 502 int num_handles) 503 { 504 CHECK_BTGATT_INIT(); 505 btif_gatts_cb_t btif_cb; 506 btif_cb.server_if = (uint8_t) server_if; 507 btif_cb.num_handles = (uint8_t) num_handles; 508 memcpy(&btif_cb.srvc_id, srvc_id, sizeof(btgatt_srvc_id_t)); 509 return btif_transfer_context(btgatts_handle_event, BTIF_GATTS_CREATE_SERVICE, 510 (char*) &btif_cb, sizeof(btif_gatts_cb_t), NULL); 511 } 512 513 static bt_status_t btif_gatts_add_included_service(int server_if, int service_handle, 514 int included_handle) 515 { 516 CHECK_BTGATT_INIT(); 517 btif_gatts_cb_t btif_cb; 518 btif_cb.server_if = (uint8_t) server_if; 519 btif_cb.srvc_handle = (uint16_t) service_handle; 520 btif_cb.incl_handle = (uint16_t) included_handle; 521 return btif_transfer_context(btgatts_handle_event, BTIF_GATTS_ADD_INCLUDED_SERVICE, 522 (char*) &btif_cb, sizeof(btif_gatts_cb_t), NULL); 523 } 524 525 static bt_status_t btif_gatts_add_characteristic(int server_if, int service_handle, 526 bt_uuid_t *uuid, int properties, 527 int permissions) 528 { 529 CHECK_BTGATT_INIT(); 530 btif_gatts_cb_t btif_cb; 531 btif_cb.server_if = (uint8_t) server_if; 532 btif_cb.srvc_handle = (uint16_t) service_handle; 533 btif_cb.properties = (uint8_t) properties; 534 btif_cb.permissions = (uint16_t) permissions; 535 memcpy(&btif_cb.uuid, uuid, sizeof(bt_uuid_t)); 536 return btif_transfer_context(btgatts_handle_event, BTIF_GATTS_ADD_CHARACTERISTIC, 537 (char*) &btif_cb, sizeof(btif_gatts_cb_t), NULL); 538 } 539 540 static bt_status_t btif_gatts_add_descriptor(int server_if, int service_handle, bt_uuid_t *uuid, 541 int permissions) 542 { 543 CHECK_BTGATT_INIT(); 544 btif_gatts_cb_t btif_cb; 545 btif_cb.server_if = (uint8_t) server_if; 546 btif_cb.srvc_handle = (uint16_t) service_handle; 547 btif_cb.permissions = (uint16_t) permissions; 548 memcpy(&btif_cb.uuid, uuid, sizeof(bt_uuid_t)); 549 return btif_transfer_context(btgatts_handle_event, BTIF_GATTS_ADD_DESCRIPTOR, 550 (char*) &btif_cb, sizeof(btif_gatts_cb_t), NULL); 551 } 552 553 static bt_status_t btif_gatts_start_service(int server_if, int service_handle, int transport) 554 { 555 CHECK_BTGATT_INIT(); 556 btif_gatts_cb_t btif_cb; 557 btif_cb.server_if = (uint8_t) server_if; 558 btif_cb.srvc_handle = (uint16_t) service_handle; 559 btif_cb.transport = (uint8_t) transport; 560 return btif_transfer_context(btgatts_handle_event, BTIF_GATTS_START_SERVICE, 561 (char*) &btif_cb, sizeof(btif_gatts_cb_t), NULL); 562 } 563 564 static bt_status_t btif_gatts_stop_service(int server_if, int service_handle) 565 { 566 CHECK_BTGATT_INIT(); 567 btif_gatts_cb_t btif_cb; 568 btif_cb.server_if = (uint8_t) server_if; 569 btif_cb.srvc_handle = (uint16_t) service_handle; 570 return btif_transfer_context(btgatts_handle_event, BTIF_GATTS_STOP_SERVICE, 571 (char*) &btif_cb, sizeof(btif_gatts_cb_t), NULL); 572 } 573 574 static bt_status_t btif_gatts_delete_service(int server_if, int service_handle) 575 { 576 CHECK_BTGATT_INIT(); 577 btif_gatts_cb_t btif_cb; 578 btif_cb.server_if = (uint8_t) server_if; 579 btif_cb.srvc_handle = (uint16_t) service_handle; 580 return btif_transfer_context(btgatts_handle_event, BTIF_GATTS_DELETE_SERVICE, 581 (char*) &btif_cb, sizeof(btif_gatts_cb_t), NULL); 582 } 583 584 static bt_status_t btif_gatts_send_indication(int server_if, int attribute_handle, int conn_id, 585 int len, int confirm, char* p_value) 586 { 587 CHECK_BTGATT_INIT(); 588 btif_gatts_cb_t btif_cb; 589 btif_cb.server_if = (uint8_t) server_if; 590 btif_cb.conn_id = (uint16_t) conn_id; 591 btif_cb.attr_handle = attribute_handle; 592 btif_cb.confirm = confirm; 593 btif_cb.len = len; 594 memcpy(btif_cb.value, p_value, len > BTGATT_MAX_ATTR_LEN ? BTGATT_MAX_ATTR_LEN : len); 595 return btif_transfer_context(btgatts_handle_event, BTIF_GATTS_SEND_INDICATION, 596 (char*) &btif_cb, sizeof(btif_gatts_cb_t), NULL); 597 } 598 599 static bt_status_t btif_gatts_send_response(int conn_id, int trans_id, 600 int status, btgatt_response_t *response) 601 { 602 CHECK_BTGATT_INIT(); 603 btif_gatts_cb_t btif_cb; 604 btif_cb.conn_id = (uint16_t) conn_id; 605 btif_cb.trans_id = (uint32_t) trans_id; 606 btif_cb.status = (uint8_t) status; 607 memcpy(&btif_cb.response, response, sizeof(btgatt_response_t)); 608 return btif_transfer_context(btgatts_handle_event, BTIF_GATTS_SEND_RESPONSE, 609 (char*) &btif_cb, sizeof(btif_gatts_cb_t), NULL); 610 } 611 612 const btgatt_server_interface_t btgattServerInterface = { 613 btif_gatts_register_app, 614 btif_gatts_unregister_app, 615 btif_gatts_open, 616 btif_gatts_close, 617 btif_gatts_add_service, 618 btif_gatts_add_included_service, 619 btif_gatts_add_characteristic, 620 btif_gatts_add_descriptor, 621 btif_gatts_start_service, 622 btif_gatts_stop_service, 623 btif_gatts_delete_service, 624 btif_gatts_send_indication, 625 btif_gatts_send_response 626 }; 627 628 #endif 629