1 /****************************************************************************** 2 * 3 * Copyright (C) 2008-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 server attributes access request 22 * handling functions. 23 * 24 ******************************************************************************/ 25 26 #include "bt_target.h" 27 #include "bt_utils.h" 28 29 #include "gatt_api.h" 30 #include "gatt_int.h" 31 32 #if BLE_INCLUDED == TRUE 33 34 #define GATTP_MAX_NUM_INC_SVR 0 35 #define GATTP_MAX_CHAR_NUM 2 36 #define GATTP_MAX_ATTR_NUM (GATTP_MAX_CHAR_NUM * 2 + GATTP_MAX_NUM_INC_SVR + 1) 37 #define GATTP_MAX_CHAR_VALUE_SIZE 50 38 39 #ifndef GATTP_ATTR_DB_SIZE 40 #define GATTP_ATTR_DB_SIZE GATT_DB_MEM_SIZE(GATTP_MAX_NUM_INC_SVR, GATTP_MAX_CHAR_NUM, GATTP_MAX_CHAR_VALUE_SIZE) 41 #endif 42 43 static void gatt_request_cback(UINT16 conn_id, UINT32 trans_id, UINT8 op_code, tGATTS_DATA *p_data); 44 static void gatt_connect_cback(tGATT_IF gatt_if, BD_ADDR bda, UINT16 conn_id, BOOLEAN connected, 45 tGATT_DISCONN_REASON reason, tBT_TRANSPORT transport); 46 static void gatt_disc_res_cback(UINT16 conn_id, tGATT_DISC_TYPE disc_type, tGATT_DISC_RES *p_data); 47 static void gatt_disc_cmpl_cback(UINT16 conn_id, tGATT_DISC_TYPE disc_type, tGATT_STATUS status); 48 static void gatt_cl_op_cmpl_cback(UINT16 conn_id, tGATTC_OPTYPE op, tGATT_STATUS status, 49 tGATT_CL_COMPLETE *p_data); 50 51 static void gatt_cl_start_config_ccc(tGATT_PROFILE_CLCB *p_clcb); 52 53 54 static tGATT_CBACK gatt_profile_cback = 55 { 56 gatt_connect_cback, 57 gatt_cl_op_cmpl_cback, 58 gatt_disc_res_cback, 59 gatt_disc_cmpl_cback, 60 gatt_request_cback, 61 NULL, 62 NULL 63 } ; 64 65 /******************************************************************************* 66 ** 67 ** Function gatt_profile_find_conn_id_by_bd_addr 68 ** 69 ** Description Find the connection ID by remote address 70 ** 71 ** Returns Connection ID 72 ** 73 *******************************************************************************/ 74 UINT16 gatt_profile_find_conn_id_by_bd_addr(BD_ADDR remote_bda) 75 { 76 UINT16 conn_id = GATT_INVALID_CONN_ID; 77 GATT_GetConnIdIfConnected (gatt_cb.gatt_if, remote_bda, &conn_id, BT_TRANSPORT_LE); 78 return conn_id; 79 } 80 81 /******************************************************************************* 82 ** 83 ** Function gatt_profile_find_clcb_by_conn_id 84 ** 85 ** Description find clcb by Connection ID 86 ** 87 ** Returns Pointer to the found link conenction control block. 88 ** 89 *******************************************************************************/ 90 static tGATT_PROFILE_CLCB *gatt_profile_find_clcb_by_conn_id(UINT16 conn_id) 91 { 92 UINT8 i_clcb; 93 tGATT_PROFILE_CLCB *p_clcb = NULL; 94 95 for (i_clcb = 0, p_clcb= gatt_cb.profile_clcb; i_clcb < GATT_MAX_APPS; i_clcb++, p_clcb++) 96 { 97 if (p_clcb->in_use && p_clcb->conn_id == conn_id) 98 return p_clcb; 99 } 100 101 return p_clcb; 102 } 103 104 /******************************************************************************* 105 ** 106 ** Function gatt_profile_find_clcb_by_bd_addr 107 ** 108 ** Description The function searches all LCBs with macthing bd address. 109 ** 110 ** Returns Pointer to the found link conenction control block. 111 ** 112 *******************************************************************************/ 113 static tGATT_PROFILE_CLCB *gatt_profile_find_clcb_by_bd_addr(BD_ADDR bda, tBT_TRANSPORT transport) 114 { 115 UINT8 i_clcb; 116 tGATT_PROFILE_CLCB *p_clcb = NULL; 117 118 for (i_clcb = 0, p_clcb= gatt_cb.profile_clcb; i_clcb < GATT_MAX_APPS; i_clcb++, p_clcb++) 119 { 120 if (p_clcb->in_use && p_clcb->transport == transport && 121 p_clcb->connected && !memcmp(p_clcb->bda, bda, BD_ADDR_LEN)) 122 return p_clcb; 123 } 124 125 return p_clcb; 126 } 127 128 /******************************************************************************* 129 ** 130 ** Function gatt_profile_clcb_alloc 131 ** 132 ** Description The function allocates a GATT profile connection link control block 133 ** 134 ** Returns NULL if not found. Otherwise pointer to the connection link block. 135 ** 136 *******************************************************************************/ 137 tGATT_PROFILE_CLCB *gatt_profile_clcb_alloc (UINT16 conn_id, BD_ADDR bda, tBT_TRANSPORT tranport) 138 { 139 UINT8 i_clcb = 0; 140 tGATT_PROFILE_CLCB *p_clcb = NULL; 141 142 for (i_clcb = 0, p_clcb= gatt_cb.profile_clcb; i_clcb < GATT_MAX_APPS; i_clcb++, p_clcb++) 143 { 144 if (!p_clcb->in_use) 145 { 146 p_clcb->in_use = TRUE; 147 p_clcb->conn_id = conn_id; 148 p_clcb->connected = TRUE; 149 p_clcb->transport = tranport; 150 memcpy (p_clcb->bda, bda, BD_ADDR_LEN); 151 break; 152 } 153 } 154 return p_clcb; 155 } 156 157 /******************************************************************************* 158 ** 159 ** Function gatt_profile_clcb_dealloc 160 ** 161 ** Description The function deallocates a GATT profile connection link control block 162 ** 163 ** Returns void 164 ** 165 *******************************************************************************/ 166 void gatt_profile_clcb_dealloc (tGATT_PROFILE_CLCB *p_clcb) 167 { 168 memset(p_clcb, 0, sizeof(tGATT_PROFILE_CLCB)); 169 } 170 171 /******************************************************************************* 172 ** 173 ** Function gatt_request_cback 174 ** 175 ** Description GATT profile attribute access request callback. 176 ** 177 ** Returns void. 178 ** 179 *******************************************************************************/ 180 static void gatt_request_cback (UINT16 conn_id, UINT32 trans_id, tGATTS_REQ_TYPE type, 181 tGATTS_DATA *p_data) 182 { 183 UINT8 status = GATT_INVALID_PDU; 184 tGATTS_RSP rsp_msg ; 185 BOOLEAN ignore = FALSE; 186 187 memset(&rsp_msg, 0, sizeof(tGATTS_RSP)); 188 189 switch (type) 190 { 191 case GATTS_REQ_TYPE_READ: 192 status = GATT_READ_NOT_PERMIT; 193 break; 194 195 case GATTS_REQ_TYPE_WRITE: 196 status = GATT_WRITE_NOT_PERMIT; 197 break; 198 199 case GATTS_REQ_TYPE_WRITE_EXEC: 200 case GATT_CMD_WRITE: 201 ignore = TRUE; 202 GATT_TRACE_EVENT("Ignore GATT_REQ_EXEC_WRITE/WRITE_CMD" ); 203 break; 204 205 case GATTS_REQ_TYPE_MTU: 206 GATT_TRACE_EVENT("Get MTU exchange new mtu size: %d", p_data->mtu); 207 ignore = TRUE; 208 break; 209 210 default: 211 GATT_TRACE_EVENT("Unknown/unexpected LE GAP ATT request: 0x%02x", type); 212 break; 213 } 214 215 if (!ignore) 216 GATTS_SendRsp (conn_id, trans_id, status, &rsp_msg); 217 218 } 219 220 /******************************************************************************* 221 ** 222 ** Function gatt_connect_cback 223 ** 224 ** Description Gatt profile connection callback. 225 ** 226 ** Returns void 227 ** 228 *******************************************************************************/ 229 static void gatt_connect_cback (tGATT_IF gatt_if, BD_ADDR bda, UINT16 conn_id, 230 BOOLEAN connected, tGATT_DISCONN_REASON reason, 231 tBT_TRANSPORT transport) 232 { 233 UNUSED(gatt_if); 234 235 GATT_TRACE_EVENT ("%s: from %08x%04x connected:%d conn_id=%d reason = 0x%04x", __FUNCTION__, 236 (bda[0]<<24)+(bda[1]<<16)+(bda[2]<<8)+bda[3], 237 (bda[4]<<8)+bda[5], connected, conn_id, reason); 238 239 tGATT_PROFILE_CLCB *p_clcb = gatt_profile_find_clcb_by_bd_addr(bda, transport); 240 if (p_clcb == NULL) 241 return; 242 243 if (connected) 244 { 245 p_clcb->conn_id = conn_id; 246 p_clcb->connected = TRUE; 247 248 if (p_clcb->ccc_stage == GATT_SVC_CHANGED_CONNECTING) 249 { 250 p_clcb->ccc_stage ++; 251 gatt_cl_start_config_ccc(p_clcb); 252 } 253 } else { 254 gatt_profile_clcb_dealloc(p_clcb); 255 } 256 } 257 258 /******************************************************************************* 259 ** 260 ** Function gatt_profile_db_init 261 ** 262 ** Description Initializa the GATT profile attribute database. 263 ** 264 *******************************************************************************/ 265 void gatt_profile_db_init (void) 266 { 267 tBT_UUID app_uuid = {LEN_UUID_128, {0}}; 268 tBT_UUID uuid = {LEN_UUID_16, {UUID_SERVCLASS_GATT_SERVER}}; 269 UINT16 service_handle = 0; 270 tGATT_STATUS status; 271 272 /* Fill our internal UUID with a fixed pattern 0x81 */ 273 memset (&app_uuid.uu.uuid128, 0x81, LEN_UUID_128); 274 275 276 /* Create a GATT profile service */ 277 gatt_cb.gatt_if = GATT_Register(&app_uuid, &gatt_profile_cback); 278 GATT_StartIf(gatt_cb.gatt_if); 279 280 service_handle = GATTS_CreateService (gatt_cb.gatt_if , &uuid, 0, GATTP_MAX_ATTR_NUM, TRUE); 281 /* add Service Changed characteristic 282 */ 283 uuid.uu.uuid16 = gatt_cb.gattp_attr.uuid = GATT_UUID_GATT_SRV_CHGD; 284 gatt_cb.gattp_attr.service_change = 0; 285 gatt_cb.gattp_attr.handle = 286 gatt_cb.handle_of_h_r = GATTS_AddCharacteristic(service_handle, &uuid, 0, GATT_CHAR_PROP_BIT_INDICATE); 287 288 GATT_TRACE_DEBUG ("gatt_profile_db_init: handle of service changed%d", 289 gatt_cb.handle_of_h_r ); 290 291 /* start service 292 */ 293 status = GATTS_StartService (gatt_cb.gatt_if, service_handle, GATTP_TRANSPORT_SUPPORTED ); 294 295 GATT_TRACE_DEBUG ("gatt_profile_db_init: gatt_if=%d start status%d", 296 gatt_cb.gatt_if, status); 297 } 298 299 /******************************************************************************* 300 ** 301 ** Function gatt_config_ccc_complete 302 ** 303 ** Description The function finish the service change ccc configuration 304 ** 305 ** Returns void 306 ** 307 *******************************************************************************/ 308 static void gatt_config_ccc_complete(tGATT_PROFILE_CLCB *p_clcb) 309 { 310 GATT_Disconnect(p_clcb->conn_id); 311 gatt_profile_clcb_dealloc(p_clcb); 312 } 313 314 /******************************************************************************* 315 ** 316 ** Function gatt_disc_res_cback 317 ** 318 ** Description Gatt profile discovery result callback 319 ** 320 ** Returns void 321 ** 322 *******************************************************************************/ 323 static void gatt_disc_res_cback (UINT16 conn_id, tGATT_DISC_TYPE disc_type, tGATT_DISC_RES *p_data) 324 { 325 tGATT_PROFILE_CLCB *p_clcb = gatt_profile_find_clcb_by_conn_id(conn_id); 326 327 if (p_clcb == NULL) 328 return; 329 330 switch (disc_type) 331 { 332 case GATT_DISC_SRVC_BY_UUID:/* stage 1 */ 333 p_clcb->e_handle = p_data->value.group_value.e_handle; 334 p_clcb->ccc_result ++; 335 break; 336 337 case GATT_DISC_CHAR:/* stage 2 */ 338 p_clcb->s_handle = p_data->value.dclr_value.val_handle; 339 p_clcb->ccc_result ++; 340 break; 341 342 case GATT_DISC_CHAR_DSCPT: /* stage 3 */ 343 if (p_data->type.uu.uuid16 == GATT_UUID_CHAR_CLIENT_CONFIG) 344 { 345 p_clcb->s_handle = p_data->handle; 346 p_clcb->ccc_result ++; 347 } 348 break; 349 } 350 } 351 352 /******************************************************************************* 353 ** 354 ** Function gatt_disc_cmpl_cback 355 ** 356 ** Description Gatt profile discovery complete callback 357 ** 358 ** Returns void 359 ** 360 *******************************************************************************/ 361 static void gatt_disc_cmpl_cback (UINT16 conn_id, tGATT_DISC_TYPE disc_type, tGATT_STATUS status) 362 { 363 tGATT_PROFILE_CLCB *p_clcb = gatt_profile_find_clcb_by_conn_id(conn_id); 364 365 if (p_clcb == NULL) 366 return; 367 368 if (status == GATT_SUCCESS && p_clcb->ccc_result > 0) 369 { 370 p_clcb->ccc_result = 0; 371 p_clcb->ccc_stage ++; 372 gatt_cl_start_config_ccc(p_clcb); 373 } else { 374 GATT_TRACE_ERROR("%s() - Register for service changed indication failure", __FUNCTION__); 375 } 376 } 377 378 /******************************************************************************* 379 ** 380 ** Function gatt_cl_op_cmpl_cback 381 ** 382 ** Description Gatt profile client operation complete callback 383 ** 384 ** Returns void 385 ** 386 *******************************************************************************/ 387 static void gatt_cl_op_cmpl_cback (UINT16 conn_id, tGATTC_OPTYPE op, 388 tGATT_STATUS status, tGATT_CL_COMPLETE *p_data) 389 { 390 tGATT_PROFILE_CLCB *p_clcb = gatt_profile_find_clcb_by_conn_id(conn_id); 391 392 if (p_clcb == NULL) 393 return; 394 395 if (op == GATTC_OPTYPE_WRITE) 396 { 397 GATT_TRACE_DEBUG("%s() - ccc write status : %d", __FUNCTION__, status); 398 } 399 400 /* free the connection */ 401 gatt_config_ccc_complete (p_clcb); 402 } 403 404 /******************************************************************************* 405 ** 406 ** Function gatt_cl_start_config_ccc 407 ** 408 ** Description Gatt profile start configure service change CCC 409 ** 410 ** Returns void 411 ** 412 *******************************************************************************/ 413 static void gatt_cl_start_config_ccc(tGATT_PROFILE_CLCB *p_clcb) 414 { 415 tGATT_DISC_PARAM srvc_disc_param; 416 tGATT_VALUE ccc_value; 417 418 GATT_TRACE_DEBUG("%s() - stage: %d", __FUNCTION__, p_clcb->ccc_stage); 419 420 memset (&srvc_disc_param, 0 , sizeof(tGATT_DISC_PARAM)); 421 memset (&ccc_value, 0 , sizeof(tGATT_VALUE)); 422 423 switch(p_clcb->ccc_stage) 424 { 425 case GATT_SVC_CHANGED_SERVICE: /* discover GATT service */ 426 srvc_disc_param.s_handle = 1; 427 srvc_disc_param.e_handle = 0xffff; 428 srvc_disc_param.service.len = 2; 429 srvc_disc_param.service.uu.uuid16 = UUID_SERVCLASS_GATT_SERVER; 430 if (GATTC_Discover (p_clcb->conn_id, GATT_DISC_SRVC_BY_UUID, &srvc_disc_param) != GATT_SUCCESS) 431 { 432 GATT_TRACE_ERROR("%s() - ccc service error", __FUNCTION__); 433 gatt_config_ccc_complete(p_clcb); 434 } 435 break; 436 437 case GATT_SVC_CHANGED_CHARACTERISTIC: /* discover service change char */ 438 srvc_disc_param.s_handle = 1; 439 srvc_disc_param.e_handle = p_clcb->e_handle; 440 srvc_disc_param.service.len = 2; 441 srvc_disc_param.service.uu.uuid16 = GATT_UUID_GATT_SRV_CHGD; 442 if (GATTC_Discover (p_clcb->conn_id, GATT_DISC_CHAR, &srvc_disc_param) != GATT_SUCCESS) 443 { 444 GATT_TRACE_ERROR("%s() - ccc char error", __FUNCTION__); 445 gatt_config_ccc_complete(p_clcb); 446 } 447 break; 448 449 case GATT_SVC_CHANGED_DESCRIPTOR: /* discover service change ccc */ 450 srvc_disc_param.s_handle = p_clcb->s_handle; 451 srvc_disc_param.e_handle = p_clcb->e_handle; 452 if (GATTC_Discover (p_clcb->conn_id, GATT_DISC_CHAR_DSCPT, &srvc_disc_param) != GATT_SUCCESS) 453 { 454 GATT_TRACE_ERROR("%s() - ccc char descriptor error", __FUNCTION__); 455 gatt_config_ccc_complete(p_clcb); 456 } 457 break; 458 459 case GATT_SVC_CHANGED_CONFIGURE_CCCD: /* write ccc */ 460 ccc_value.handle = p_clcb->s_handle; 461 ccc_value.len = 2; 462 ccc_value.value[0] = GATT_CLT_CONFIG_INDICATION; 463 if (GATTC_Write (p_clcb->conn_id, GATT_WRITE, &ccc_value) != GATT_SUCCESS) 464 { 465 GATT_TRACE_ERROR("%s() - write ccc error", __FUNCTION__); 466 gatt_config_ccc_complete(p_clcb); 467 } 468 break; 469 } 470 } 471 472 /******************************************************************************* 473 ** 474 ** Function GATT_ConfigServiceChangeCCC 475 ** 476 ** Description Configure service change indication on remote device 477 ** 478 ** Returns none 479 ** 480 *******************************************************************************/ 481 void GATT_ConfigServiceChangeCCC (BD_ADDR remote_bda, BOOLEAN enable, tBT_TRANSPORT transport) 482 { 483 UINT16 conn_id = GATT_INVALID_CONN_ID; 484 tGATT_PROFILE_CLCB *p_clcb = gatt_profile_find_clcb_by_bd_addr (remote_bda, transport); 485 486 if (p_clcb == NULL) 487 p_clcb = gatt_profile_clcb_alloc (0, remote_bda, transport); 488 489 if (p_clcb == NULL) 490 return; 491 492 if (GATT_GetConnIdIfConnected (gatt_cb.gatt_if, remote_bda, &p_clcb->conn_id, transport)) 493 { 494 p_clcb->connected = TRUE; 495 } 496 /* hold the link here */ 497 GATT_Connect(gatt_cb.gatt_if, remote_bda, TRUE, transport); 498 p_clcb->ccc_stage = GATT_SVC_CHANGED_CONNECTING; 499 500 if (!p_clcb->connected) 501 { 502 /* wait for connection */ 503 return; 504 } 505 506 p_clcb->ccc_stage ++; 507 gatt_cl_start_config_ccc(p_clcb); 508 } 509 510 #endif /* BLE_INCLUDED */ 511