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