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_disc_res_cback 305 ** 306 ** Description Gatt profile discovery result callback 307 ** 308 ** Returns void 309 ** 310 *******************************************************************************/ 311 static void gatt_disc_res_cback (UINT16 conn_id, tGATT_DISC_TYPE disc_type, tGATT_DISC_RES *p_data) 312 { 313 tGATT_PROFILE_CLCB *p_clcb = gatt_profile_find_clcb_by_conn_id(conn_id); 314 315 if (p_clcb == NULL) 316 return; 317 318 switch (disc_type) 319 { 320 case GATT_DISC_SRVC_BY_UUID:/* stage 1 */ 321 p_clcb->e_handle = p_data->value.group_value.e_handle; 322 p_clcb->ccc_result ++; 323 break; 324 325 case GATT_DISC_CHAR:/* stage 2 */ 326 p_clcb->s_handle = p_data->value.dclr_value.val_handle; 327 p_clcb->ccc_result ++; 328 break; 329 330 case GATT_DISC_CHAR_DSCPT: /* stage 3 */ 331 if (p_data->type.uu.uuid16 == GATT_UUID_CHAR_CLIENT_CONFIG) 332 { 333 p_clcb->s_handle = p_data->handle; 334 p_clcb->ccc_result ++; 335 } 336 break; 337 } 338 } 339 340 /******************************************************************************* 341 ** 342 ** Function gatt_disc_cmpl_cback 343 ** 344 ** Description Gatt profile discovery complete callback 345 ** 346 ** Returns void 347 ** 348 *******************************************************************************/ 349 static void gatt_disc_cmpl_cback (UINT16 conn_id, tGATT_DISC_TYPE disc_type, tGATT_STATUS status) 350 { 351 tGATT_PROFILE_CLCB *p_clcb = gatt_profile_find_clcb_by_conn_id(conn_id); 352 353 if (p_clcb == NULL) 354 return; 355 356 if (status == GATT_SUCCESS && p_clcb->ccc_result > 0) 357 { 358 p_clcb->ccc_result = 0; 359 p_clcb->ccc_stage ++; 360 gatt_cl_start_config_ccc(p_clcb); 361 } else { 362 GATT_TRACE_ERROR("%s() - Unable to register for service changed indication", __func__); 363 } 364 } 365 366 /******************************************************************************* 367 ** 368 ** Function gatt_cl_op_cmpl_cback 369 ** 370 ** Description Gatt profile client operation complete callback 371 ** 372 ** Returns void 373 ** 374 *******************************************************************************/ 375 static void gatt_cl_op_cmpl_cback (UINT16 conn_id, tGATTC_OPTYPE op, 376 tGATT_STATUS status, tGATT_CL_COMPLETE *p_data) 377 { 378 UNUSED(conn_id); 379 UNUSED(op); 380 UNUSED(status); 381 UNUSED(p_data); 382 } 383 384 /******************************************************************************* 385 ** 386 ** Function gatt_cl_start_config_ccc 387 ** 388 ** Description Gatt profile start configure service change CCC 389 ** 390 ** Returns void 391 ** 392 *******************************************************************************/ 393 static void gatt_cl_start_config_ccc(tGATT_PROFILE_CLCB *p_clcb) 394 { 395 tGATT_DISC_PARAM srvc_disc_param; 396 tGATT_VALUE ccc_value; 397 398 GATT_TRACE_DEBUG("%s() - stage: %d", __FUNCTION__, p_clcb->ccc_stage); 399 400 memset (&srvc_disc_param, 0 , sizeof(tGATT_DISC_PARAM)); 401 memset (&ccc_value, 0 , sizeof(tGATT_VALUE)); 402 403 switch(p_clcb->ccc_stage) 404 { 405 case GATT_SVC_CHANGED_SERVICE: /* discover GATT service */ 406 srvc_disc_param.s_handle = 1; 407 srvc_disc_param.e_handle = 0xffff; 408 srvc_disc_param.service.len = 2; 409 srvc_disc_param.service.uu.uuid16 = UUID_SERVCLASS_GATT_SERVER; 410 GATTC_Discover(p_clcb->conn_id, GATT_DISC_SRVC_BY_UUID, &srvc_disc_param); 411 break; 412 413 case GATT_SVC_CHANGED_CHARACTERISTIC: /* discover service change char */ 414 srvc_disc_param.s_handle = 1; 415 srvc_disc_param.e_handle = p_clcb->e_handle; 416 srvc_disc_param.service.len = 2; 417 srvc_disc_param.service.uu.uuid16 = GATT_UUID_GATT_SRV_CHGD; 418 GATTC_Discover(p_clcb->conn_id, GATT_DISC_CHAR, &srvc_disc_param); 419 break; 420 421 case GATT_SVC_CHANGED_DESCRIPTOR: /* discover service change ccc */ 422 srvc_disc_param.s_handle = p_clcb->s_handle; 423 srvc_disc_param.e_handle = p_clcb->e_handle; 424 GATTC_Discover(p_clcb->conn_id, GATT_DISC_CHAR_DSCPT, &srvc_disc_param); 425 break; 426 427 case GATT_SVC_CHANGED_CONFIGURE_CCCD: /* write ccc */ 428 ccc_value.handle = p_clcb->s_handle; 429 ccc_value.len = 2; 430 ccc_value.value[0] = GATT_CLT_CONFIG_INDICATION; 431 GATTC_Write(p_clcb->conn_id, GATT_WRITE, &ccc_value); 432 break; 433 } 434 } 435 436 /******************************************************************************* 437 ** 438 ** Function GATT_ConfigServiceChangeCCC 439 ** 440 ** Description Configure service change indication on remote device 441 ** 442 ** Returns none 443 ** 444 *******************************************************************************/ 445 void GATT_ConfigServiceChangeCCC (BD_ADDR remote_bda, BOOLEAN enable, tBT_TRANSPORT transport) 446 { 447 tGATT_PROFILE_CLCB *p_clcb = gatt_profile_find_clcb_by_bd_addr (remote_bda, transport); 448 449 if (p_clcb == NULL) 450 p_clcb = gatt_profile_clcb_alloc (0, remote_bda, transport); 451 452 if (p_clcb == NULL) 453 return; 454 455 if (GATT_GetConnIdIfConnected (gatt_cb.gatt_if, remote_bda, &p_clcb->conn_id, transport)) 456 { 457 p_clcb->connected = TRUE; 458 } 459 /* hold the link here */ 460 GATT_Connect(gatt_cb.gatt_if, remote_bda, TRUE, transport, true); 461 p_clcb->ccc_stage = GATT_SVC_CHANGED_CONNECTING; 462 463 if (!p_clcb->connected) 464 { 465 /* wait for connection */ 466 return; 467 } 468 469 p_clcb->ccc_stage ++; 470 gatt_cl_start_config_ccc(p_clcb); 471 } 472 473 #endif /* BLE_INCLUDED */ 474