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 NULL; 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 NULL; 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 if(i_clcb < GATT_MAX_APPS) 155 return p_clcb; 156 157 return NULL; 158 } 159 160 /******************************************************************************* 161 ** 162 ** Function gatt_profile_clcb_dealloc 163 ** 164 ** Description The function deallocates a GATT profile connection link control block 165 ** 166 ** Returns void 167 ** 168 *******************************************************************************/ 169 void gatt_profile_clcb_dealloc (tGATT_PROFILE_CLCB *p_clcb) 170 { 171 memset(p_clcb, 0, sizeof(tGATT_PROFILE_CLCB)); 172 } 173 174 /******************************************************************************* 175 ** 176 ** Function gatt_request_cback 177 ** 178 ** Description GATT profile attribute access request callback. 179 ** 180 ** Returns void. 181 ** 182 *******************************************************************************/ 183 static void gatt_request_cback (UINT16 conn_id, UINT32 trans_id, tGATTS_REQ_TYPE type, 184 tGATTS_DATA *p_data) 185 { 186 UINT8 status = GATT_INVALID_PDU; 187 tGATTS_RSP rsp_msg ; 188 BOOLEAN ignore = FALSE; 189 190 memset(&rsp_msg, 0, sizeof(tGATTS_RSP)); 191 192 switch (type) 193 { 194 case GATTS_REQ_TYPE_READ: 195 status = GATT_READ_NOT_PERMIT; 196 break; 197 198 case GATTS_REQ_TYPE_WRITE: 199 status = GATT_WRITE_NOT_PERMIT; 200 break; 201 202 case GATTS_REQ_TYPE_WRITE_EXEC: 203 case GATT_CMD_WRITE: 204 ignore = TRUE; 205 GATT_TRACE_EVENT("Ignore GATT_REQ_EXEC_WRITE/WRITE_CMD" ); 206 break; 207 208 case GATTS_REQ_TYPE_MTU: 209 GATT_TRACE_EVENT("Get MTU exchange new mtu size: %d", p_data->mtu); 210 ignore = TRUE; 211 break; 212 213 default: 214 GATT_TRACE_EVENT("Unknown/unexpected LE GAP ATT request: 0x%02x", type); 215 break; 216 } 217 218 if (!ignore) 219 GATTS_SendRsp (conn_id, trans_id, status, &rsp_msg); 220 221 } 222 223 /******************************************************************************* 224 ** 225 ** Function gatt_connect_cback 226 ** 227 ** Description Gatt profile connection callback. 228 ** 229 ** Returns void 230 ** 231 *******************************************************************************/ 232 static void gatt_connect_cback (tGATT_IF gatt_if, BD_ADDR bda, UINT16 conn_id, 233 BOOLEAN connected, tGATT_DISCONN_REASON reason, 234 tBT_TRANSPORT transport) 235 { 236 UNUSED(gatt_if); 237 238 GATT_TRACE_EVENT ("%s: from %08x%04x connected:%d conn_id=%d reason = 0x%04x", __FUNCTION__, 239 (bda[0]<<24)+(bda[1]<<16)+(bda[2]<<8)+bda[3], 240 (bda[4]<<8)+bda[5], connected, conn_id, reason); 241 242 tGATT_PROFILE_CLCB *p_clcb = gatt_profile_find_clcb_by_bd_addr(bda, transport); 243 if (p_clcb == NULL) 244 return; 245 246 if (connected) 247 { 248 p_clcb->conn_id = conn_id; 249 p_clcb->connected = TRUE; 250 251 if (p_clcb->ccc_stage == GATT_SVC_CHANGED_CONNECTING) 252 { 253 p_clcb->ccc_stage ++; 254 gatt_cl_start_config_ccc(p_clcb); 255 } 256 } else { 257 gatt_profile_clcb_dealloc(p_clcb); 258 } 259 } 260 261 /******************************************************************************* 262 ** 263 ** Function gatt_profile_db_init 264 ** 265 ** Description Initializa the GATT profile attribute database. 266 ** 267 *******************************************************************************/ 268 void gatt_profile_db_init (void) 269 { 270 tBT_UUID app_uuid = {LEN_UUID_128, {0}}; 271 tBT_UUID uuid = {LEN_UUID_16, {UUID_SERVCLASS_GATT_SERVER}}; 272 UINT16 service_handle = 0; 273 tGATT_STATUS status; 274 275 /* Fill our internal UUID with a fixed pattern 0x81 */ 276 memset (&app_uuid.uu.uuid128, 0x81, LEN_UUID_128); 277 278 279 /* Create a GATT profile service */ 280 gatt_cb.gatt_if = GATT_Register(&app_uuid, &gatt_profile_cback); 281 GATT_StartIf(gatt_cb.gatt_if); 282 283 service_handle = GATTS_CreateService (gatt_cb.gatt_if , &uuid, 0, GATTP_MAX_ATTR_NUM, TRUE); 284 /* add Service Changed characteristic 285 */ 286 uuid.uu.uuid16 = gatt_cb.gattp_attr.uuid = GATT_UUID_GATT_SRV_CHGD; 287 gatt_cb.gattp_attr.service_change = 0; 288 gatt_cb.gattp_attr.handle = 289 gatt_cb.handle_of_h_r = GATTS_AddCharacteristic(service_handle, &uuid, 0, GATT_CHAR_PROP_BIT_INDICATE); 290 291 GATT_TRACE_DEBUG ("gatt_profile_db_init: handle of service changed%d", 292 gatt_cb.handle_of_h_r ); 293 294 /* start service 295 */ 296 status = GATTS_StartService (gatt_cb.gatt_if, service_handle, GATTP_TRANSPORT_SUPPORTED ); 297 298 GATT_TRACE_DEBUG ("gatt_profile_db_init: gatt_if=%d start status%d", 299 gatt_cb.gatt_if, status); 300 } 301 302 /******************************************************************************* 303 ** 304 ** Function gatt_config_ccc_complete 305 ** 306 ** Description The function finish the service change ccc configuration 307 ** 308 ** Returns void 309 ** 310 *******************************************************************************/ 311 static void gatt_config_ccc_complete(tGATT_PROFILE_CLCB *p_clcb) 312 { 313 GATT_Disconnect(p_clcb->conn_id); 314 gatt_profile_clcb_dealloc(p_clcb); 315 } 316 317 /******************************************************************************* 318 ** 319 ** Function gatt_disc_res_cback 320 ** 321 ** Description Gatt profile discovery result callback 322 ** 323 ** Returns void 324 ** 325 *******************************************************************************/ 326 static void gatt_disc_res_cback (UINT16 conn_id, tGATT_DISC_TYPE disc_type, tGATT_DISC_RES *p_data) 327 { 328 tGATT_PROFILE_CLCB *p_clcb = gatt_profile_find_clcb_by_conn_id(conn_id); 329 330 if (p_clcb == NULL) 331 return; 332 333 switch (disc_type) 334 { 335 case GATT_DISC_SRVC_BY_UUID:/* stage 1 */ 336 p_clcb->e_handle = p_data->value.group_value.e_handle; 337 p_clcb->ccc_result ++; 338 break; 339 340 case GATT_DISC_CHAR:/* stage 2 */ 341 p_clcb->s_handle = p_data->value.dclr_value.val_handle; 342 p_clcb->ccc_result ++; 343 break; 344 345 case GATT_DISC_CHAR_DSCPT: /* stage 3 */ 346 if (p_data->type.uu.uuid16 == GATT_UUID_CHAR_CLIENT_CONFIG) 347 { 348 p_clcb->s_handle = p_data->handle; 349 p_clcb->ccc_result ++; 350 } 351 break; 352 } 353 } 354 355 /******************************************************************************* 356 ** 357 ** Function gatt_disc_cmpl_cback 358 ** 359 ** Description Gatt profile discovery complete callback 360 ** 361 ** Returns void 362 ** 363 *******************************************************************************/ 364 static void gatt_disc_cmpl_cback (UINT16 conn_id, tGATT_DISC_TYPE disc_type, tGATT_STATUS status) 365 { 366 tGATT_PROFILE_CLCB *p_clcb = gatt_profile_find_clcb_by_conn_id(conn_id); 367 368 if (p_clcb == NULL) 369 return; 370 371 if (status == GATT_SUCCESS && p_clcb->ccc_result > 0) 372 { 373 p_clcb->ccc_result = 0; 374 p_clcb->ccc_stage ++; 375 gatt_cl_start_config_ccc(p_clcb); 376 } else { 377 GATT_TRACE_ERROR("%s() - Register for service changed indication failure", __FUNCTION__); 378 /* free the connection */ 379 gatt_config_ccc_complete (p_clcb); 380 } 381 } 382 383 /******************************************************************************* 384 ** 385 ** Function gatt_cl_op_cmpl_cback 386 ** 387 ** Description Gatt profile client operation complete callback 388 ** 389 ** Returns void 390 ** 391 *******************************************************************************/ 392 static void gatt_cl_op_cmpl_cback (UINT16 conn_id, tGATTC_OPTYPE op, 393 tGATT_STATUS status, tGATT_CL_COMPLETE *p_data) 394 { 395 tGATT_PROFILE_CLCB *p_clcb = gatt_profile_find_clcb_by_conn_id(conn_id); 396 397 if (p_clcb == NULL) 398 return; 399 400 if (op == GATTC_OPTYPE_WRITE) 401 { 402 GATT_TRACE_DEBUG("%s() - ccc write status : %d", __FUNCTION__, status); 403 } 404 405 /* free the connection */ 406 gatt_config_ccc_complete (p_clcb); 407 } 408 409 /******************************************************************************* 410 ** 411 ** Function gatt_cl_start_config_ccc 412 ** 413 ** Description Gatt profile start configure service change CCC 414 ** 415 ** Returns void 416 ** 417 *******************************************************************************/ 418 static void gatt_cl_start_config_ccc(tGATT_PROFILE_CLCB *p_clcb) 419 { 420 tGATT_DISC_PARAM srvc_disc_param; 421 tGATT_VALUE ccc_value; 422 423 GATT_TRACE_DEBUG("%s() - stage: %d", __FUNCTION__, p_clcb->ccc_stage); 424 425 memset (&srvc_disc_param, 0 , sizeof(tGATT_DISC_PARAM)); 426 memset (&ccc_value, 0 , sizeof(tGATT_VALUE)); 427 428 switch(p_clcb->ccc_stage) 429 { 430 case GATT_SVC_CHANGED_SERVICE: /* discover GATT service */ 431 srvc_disc_param.s_handle = 1; 432 srvc_disc_param.e_handle = 0xffff; 433 srvc_disc_param.service.len = 2; 434 srvc_disc_param.service.uu.uuid16 = UUID_SERVCLASS_GATT_SERVER; 435 if (GATTC_Discover (p_clcb->conn_id, GATT_DISC_SRVC_BY_UUID, &srvc_disc_param) != GATT_SUCCESS) 436 { 437 GATT_TRACE_ERROR("%s() - ccc service error", __FUNCTION__); 438 gatt_config_ccc_complete(p_clcb); 439 } 440 break; 441 442 case GATT_SVC_CHANGED_CHARACTERISTIC: /* discover service change char */ 443 srvc_disc_param.s_handle = 1; 444 srvc_disc_param.e_handle = p_clcb->e_handle; 445 srvc_disc_param.service.len = 2; 446 srvc_disc_param.service.uu.uuid16 = GATT_UUID_GATT_SRV_CHGD; 447 if (GATTC_Discover (p_clcb->conn_id, GATT_DISC_CHAR, &srvc_disc_param) != GATT_SUCCESS) 448 { 449 GATT_TRACE_ERROR("%s() - ccc char error", __FUNCTION__); 450 gatt_config_ccc_complete(p_clcb); 451 } 452 break; 453 454 case GATT_SVC_CHANGED_DESCRIPTOR: /* discover service change ccc */ 455 srvc_disc_param.s_handle = p_clcb->s_handle; 456 srvc_disc_param.e_handle = p_clcb->e_handle; 457 if (GATTC_Discover (p_clcb->conn_id, GATT_DISC_CHAR_DSCPT, &srvc_disc_param) != GATT_SUCCESS) 458 { 459 GATT_TRACE_ERROR("%s() - ccc char descriptor error", __FUNCTION__); 460 gatt_config_ccc_complete(p_clcb); 461 } 462 break; 463 464 case GATT_SVC_CHANGED_CONFIGURE_CCCD: /* write ccc */ 465 ccc_value.handle = p_clcb->s_handle; 466 ccc_value.len = 2; 467 ccc_value.value[0] = GATT_CLT_CONFIG_INDICATION; 468 if (GATTC_Write (p_clcb->conn_id, GATT_WRITE, &ccc_value) != GATT_SUCCESS) 469 { 470 GATT_TRACE_ERROR("%s() - write ccc error", __FUNCTION__); 471 gatt_config_ccc_complete(p_clcb); 472 } 473 break; 474 } 475 } 476 477 /******************************************************************************* 478 ** 479 ** Function GATT_ConfigServiceChangeCCC 480 ** 481 ** Description Configure service change indication on remote device 482 ** 483 ** Returns none 484 ** 485 *******************************************************************************/ 486 void GATT_ConfigServiceChangeCCC (BD_ADDR remote_bda, BOOLEAN enable, tBT_TRANSPORT transport) 487 { 488 UINT16 conn_id = GATT_INVALID_CONN_ID; 489 tGATT_PROFILE_CLCB *p_clcb = gatt_profile_find_clcb_by_bd_addr (remote_bda, transport); 490 491 if (p_clcb == NULL) 492 p_clcb = gatt_profile_clcb_alloc (0, remote_bda, transport); 493 494 if (p_clcb == NULL) 495 return; 496 497 if (GATT_GetConnIdIfConnected (gatt_cb.gatt_if, remote_bda, &p_clcb->conn_id, transport)) 498 { 499 p_clcb->connected = TRUE; 500 } 501 /* hold the link here */ 502 GATT_Connect(gatt_cb.gatt_if, remote_bda, TRUE, transport); 503 p_clcb->ccc_stage = GATT_SVC_CHANGED_CONNECTING; 504 505 if (!p_clcb->connected) 506 { 507 /* wait for connection */ 508 return; 509 } 510 511 p_clcb->ccc_stage ++; 512 gatt_cl_start_config_ccc(p_clcb); 513 } 514 515 #endif /* BLE_INCLUDED */ 516