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 43static void gatt_request_cback(UINT16 conn_id, UINT32 trans_id, UINT8 op_code, tGATTS_DATA *p_data); 44static void gatt_connect_cback(tGATT_IF gatt_if, BD_ADDR bda, UINT16 conn_id, BOOLEAN connected, 45 tGATT_DISCONN_REASON reason, tBT_TRANSPORT transport); 46static void gatt_disc_res_cback(UINT16 conn_id, tGATT_DISC_TYPE disc_type, tGATT_DISC_RES *p_data); 47static void gatt_disc_cmpl_cback(UINT16 conn_id, tGATT_DISC_TYPE disc_type, tGATT_STATUS status); 48static void gatt_cl_op_cmpl_cback(UINT16 conn_id, tGATTC_OPTYPE op, tGATT_STATUS status, 49 tGATT_CL_COMPLETE *p_data); 50 51static void gatt_cl_start_config_ccc(tGATT_PROFILE_CLCB *p_clcb); 52 53 54static 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*******************************************************************************/ 74UINT16 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*******************************************************************************/ 90static 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*******************************************************************************/ 113static 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*******************************************************************************/ 137tGATT_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*******************************************************************************/ 169void 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*******************************************************************************/ 183static 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*******************************************************************************/ 232static 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*******************************************************************************/ 268void 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*******************************************************************************/ 311static 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*******************************************************************************/ 326static 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*******************************************************************************/ 364static 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*******************************************************************************/ 392static 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*******************************************************************************/ 418static 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*******************************************************************************/ 486void 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