Home | History | Annotate | Download | only in gatt
      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