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 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