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 ATT protocol functions
     22  *
     23  ******************************************************************************/
     24 
     25 #include "bt_target.h"
     26 
     27 #if BLE_INCLUDED == TRUE
     28 
     29 #include "gatt_int.h"
     30 #include "l2c_api.h"
     31 
     32 #define GATT_HDR_FIND_TYPE_VALUE_LEN    21
     33 #define GATT_OP_CODE_SIZE   1
     34 /**********************************************************************
     35 **   ATT protocl message building utility                              *
     36 ***********************************************************************/
     37 /*******************************************************************************
     38 **
     39 ** Function         attp_build_mtu_exec_cmd
     40 **
     41 ** Description      Build a exchange MTU request
     42 **
     43 ** Returns          None.
     44 **
     45 *******************************************************************************/
     46 BT_HDR *attp_build_mtu_cmd(UINT8 op_code, UINT16 rx_mtu)
     47 {
     48     BT_HDR      *p_buf = NULL;
     49     UINT8       *p;
     50 
     51     if ((p_buf = (BT_HDR *)GKI_getbuf(sizeof(BT_HDR) + GATT_HDR_SIZE + L2CAP_MIN_OFFSET)) != NULL)
     52     {
     53         p = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET;
     54 
     55         UINT8_TO_STREAM (p, op_code);
     56         UINT16_TO_STREAM (p, rx_mtu);
     57 
     58         p_buf->offset = L2CAP_MIN_OFFSET;
     59         p_buf->len = GATT_HDR_SIZE; /* opcode + 2 bytes mtu */
     60     }
     61     return p_buf;
     62 }
     63 /*******************************************************************************
     64 **
     65 ** Function         attp_build_exec_write_cmd
     66 **
     67 ** Description      Build a execute write request or response.
     68 **
     69 ** Returns          None.
     70 **
     71 *******************************************************************************/
     72 BT_HDR *attp_build_exec_write_cmd (UINT8 op_code, UINT8 flag)
     73 {
     74     BT_HDR      *p_buf = NULL;
     75     UINT8       *p;
     76 
     77     if ((p_buf = (BT_HDR *)GKI_getbuf((UINT16)(sizeof(BT_HDR) + 10 + L2CAP_MIN_OFFSET))) != NULL)
     78     {
     79         p = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET;
     80 
     81         p_buf->offset = L2CAP_MIN_OFFSET;
     82         p_buf->len = GATT_OP_CODE_SIZE;
     83 
     84         UINT8_TO_STREAM (p, op_code);
     85 
     86         if (op_code == GATT_REQ_EXEC_WRITE)
     87         {
     88             flag &= GATT_PREP_WRITE_EXEC;
     89             UINT8_TO_STREAM (p, flag);
     90             p_buf->len += 1;
     91         }
     92 
     93     }
     94 
     95     return p_buf;
     96 }
     97 
     98 /*******************************************************************************
     99 **
    100 ** Function         attp_build_err_cmd
    101 **
    102 ** Description      Build a exchange MTU request
    103 **
    104 ** Returns          None.
    105 **
    106 *******************************************************************************/
    107 BT_HDR *attp_build_err_cmd(UINT8 cmd_code, UINT16 err_handle, UINT8 reason)
    108 {
    109     BT_HDR      *p_buf = NULL;
    110     UINT8       *p;
    111 
    112     if ((p_buf = (BT_HDR *)GKI_getbuf(sizeof(BT_HDR) + L2CAP_MIN_OFFSET + 5)) != NULL)
    113     {
    114         p = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET;
    115 
    116         UINT8_TO_STREAM (p, GATT_RSP_ERROR);
    117         UINT8_TO_STREAM (p, cmd_code);
    118         UINT16_TO_STREAM(p, err_handle);
    119         UINT8_TO_STREAM (p, reason);
    120 
    121         p_buf->offset = L2CAP_MIN_OFFSET;
    122         /* GATT_HDR_SIZE (1B ERR_RSP op code+ 2B handle) + 1B cmd_op_code  + 1B status */
    123         p_buf->len = GATT_HDR_SIZE + 1 + 1;
    124     }
    125     return p_buf;
    126 }
    127 /*******************************************************************************
    128 **
    129 ** Function         attp_build_browse_cmd
    130 **
    131 ** Description      Build a read information request or read by type request
    132 **
    133 ** Returns          None.
    134 **
    135 *******************************************************************************/
    136 BT_HDR *attp_build_browse_cmd(UINT8 op_code, UINT16 s_hdl, UINT16 e_hdl, tBT_UUID uuid)
    137 {
    138     BT_HDR      *p_buf = NULL;
    139     UINT8       *p;
    140 
    141     if ((p_buf = (BT_HDR *)GKI_getbuf(sizeof(BT_HDR) + 8 + L2CAP_MIN_OFFSET)) != NULL)
    142     {
    143         p = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET;
    144         /* Describe the built message location and size */
    145         p_buf->offset = L2CAP_MIN_OFFSET;
    146         p_buf->len = GATT_OP_CODE_SIZE + 4;
    147 
    148         UINT8_TO_STREAM (p, op_code);
    149         UINT16_TO_STREAM (p, s_hdl);
    150         UINT16_TO_STREAM (p, e_hdl);
    151         p_buf->len += gatt_build_uuid_to_stream(&p, uuid);
    152     }
    153 
    154     return p_buf;
    155 }
    156 /*******************************************************************************
    157 **
    158 ** Function         attp_build_read_handles_cmd
    159 **
    160 ** Description      Build a read by type and value request.
    161 **
    162 ** Returns          pointer to the command buffer.
    163 **
    164 *******************************************************************************/
    165 BT_HDR *attp_build_read_handles_cmd (UINT16 payload_size, tGATT_FIND_TYPE_VALUE *p_value_type)
    166 {
    167     BT_HDR      *p_buf = NULL;
    168     UINT8       *p;
    169     UINT16      len = p_value_type->value_len;
    170 
    171     if ((p_buf = (BT_HDR *)GKI_getbuf((UINT16)(sizeof(BT_HDR) + payload_size + L2CAP_MIN_OFFSET))) != NULL)
    172     {
    173         p = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET;
    174 
    175         p_buf->offset = L2CAP_MIN_OFFSET;
    176         p_buf->len = 5; /* opcode + s_handle + e_handle */
    177 
    178         UINT8_TO_STREAM  (p, GATT_REQ_FIND_TYPE_VALUE);
    179         UINT16_TO_STREAM (p, p_value_type->s_handle);
    180         UINT16_TO_STREAM (p, p_value_type->e_handle);
    181 
    182         p_buf->len += gatt_build_uuid_to_stream(&p, p_value_type->uuid);
    183 
    184         if (p_value_type->value_len +  p_buf->len > payload_size )
    185             len = payload_size - p_buf->len;
    186 
    187         memcpy (p, p_value_type->value, len);
    188         p_buf->len += len;
    189     }
    190 
    191     return p_buf;
    192 }
    193 /*******************************************************************************
    194 **
    195 ** Function         attp_build_read_multi_cmd
    196 **
    197 ** Description      Build a read multiple request
    198 **
    199 ** Returns          None.
    200 **
    201 *******************************************************************************/
    202 BT_HDR *attp_build_read_multi_cmd(UINT16 payload_size, UINT16 num_handle, UINT16 *p_handle)
    203 {
    204     BT_HDR      *p_buf = NULL;
    205     UINT8       *p, i = 0;
    206 
    207     if ((p_buf = (BT_HDR *)GKI_getbuf((UINT16)(sizeof(BT_HDR) + num_handle * 2 + 1 + L2CAP_MIN_OFFSET))) != NULL)
    208     {
    209         p = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET;
    210 
    211         p_buf->offset = L2CAP_MIN_OFFSET;
    212         p_buf->len = 1;
    213 
    214         UINT8_TO_STREAM (p, GATT_REQ_READ_MULTI);
    215 
    216         for (i = 0; i < num_handle && p_buf->len + 2 <= payload_size; i ++)
    217         {
    218             UINT16_TO_STREAM (p, *(p_handle + i));
    219             p_buf->len += 2;
    220         }
    221     }
    222 
    223     return p_buf;
    224 }
    225 /*******************************************************************************
    226 **
    227 ** Function         attp_build_handle_cmd
    228 **
    229 ** Description      Build a read /read blob request
    230 **
    231 ** Returns          None.
    232 **
    233 *******************************************************************************/
    234 BT_HDR *attp_build_handle_cmd(UINT8 op_code, UINT16 handle, UINT16 offset)
    235 {
    236     BT_HDR      *p_buf = NULL;
    237     UINT8       *p;
    238 
    239     if ((p_buf = (BT_HDR *)GKI_getbuf(sizeof(BT_HDR) + 5 + L2CAP_MIN_OFFSET)) != NULL)
    240     {
    241         p = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET;
    242 
    243         p_buf->offset = L2CAP_MIN_OFFSET;
    244 
    245         UINT8_TO_STREAM (p, op_code);
    246         p_buf->len  = 1;
    247 
    248         UINT16_TO_STREAM (p, handle);
    249         p_buf->len += 2;
    250 
    251         if (op_code == GATT_REQ_READ_BLOB)
    252         {
    253             UINT16_TO_STREAM (p, offset);
    254             p_buf->len += 2;
    255         }
    256 
    257     }
    258 
    259     return p_buf;
    260 }
    261 /*******************************************************************************
    262 **
    263 ** Function         attp_build_opcode_cmd
    264 **
    265 ** Description      Build a  request/response with opcode only.
    266 **
    267 ** Returns          None.
    268 **
    269 *******************************************************************************/
    270 BT_HDR *attp_build_opcode_cmd(UINT8 op_code)
    271 {
    272     BT_HDR      *p_buf = NULL;
    273     UINT8       *p;
    274 
    275     if ((p_buf = (BT_HDR *)GKI_getbuf(sizeof(BT_HDR) + 1 + L2CAP_MIN_OFFSET)) != NULL)
    276     {
    277         p = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET;
    278         p_buf->offset = L2CAP_MIN_OFFSET;
    279 
    280         UINT8_TO_STREAM (p, op_code);
    281         p_buf->len  = 1;
    282     }
    283 
    284     return p_buf;
    285 }
    286 /*******************************************************************************
    287 **
    288 ** Function         attp_build_value_cmd
    289 **
    290 ** Description      Build a attribute value request
    291 **
    292 ** Returns          None.
    293 **
    294 *******************************************************************************/
    295 BT_HDR *attp_build_value_cmd (UINT16 payload_size, UINT8 op_code, UINT16 handle,
    296                               UINT16 offset, UINT16 len, UINT8 *p_data)
    297 {
    298     BT_HDR      *p_buf = NULL;
    299     UINT8       *p, *pp, pair_len, *p_pair_len;
    300 
    301     if ((p_buf = (BT_HDR *)GKI_getbuf((UINT16)(sizeof(BT_HDR) + payload_size + L2CAP_MIN_OFFSET))) != NULL)
    302     {
    303         p = pp =(UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET;
    304 
    305         UINT8_TO_STREAM (p, op_code);
    306         p_buf->offset = L2CAP_MIN_OFFSET;
    307         p_buf->len = 1;
    308 
    309         if (op_code == GATT_RSP_READ_BY_TYPE)
    310         {
    311             p_pair_len = p;
    312             pair_len = len + 2;
    313             UINT8_TO_STREAM (p, pair_len);
    314             p_buf->len += 1;
    315         }
    316         if (op_code != GATT_RSP_READ_BLOB && op_code != GATT_RSP_READ)
    317         {
    318             UINT16_TO_STREAM (p, handle);
    319             p_buf->len += 2;
    320         }
    321 
    322         if (op_code == GATT_REQ_PREPARE_WRITE ||op_code == GATT_RSP_PREPARE_WRITE )
    323         {
    324             UINT16_TO_STREAM (p, offset);
    325             p_buf->len += 2;
    326         }
    327 
    328         if (len > 0 && p_data != NULL)
    329         {
    330             /* ensure data not exceed MTU size */
    331             if (payload_size - p_buf->len < len)
    332             {
    333                 len = payload_size - p_buf->len;
    334                 /* update handle value pair length */
    335                 if (op_code == GATT_RSP_READ_BY_TYPE)
    336                     *p_pair_len = (len + 2);
    337 
    338                 GATT_TRACE_WARNING1("attribute value too long, to be truncated to %d", len);
    339             }
    340 
    341             ARRAY_TO_STREAM (p, p_data, len);
    342             p_buf->len += len;
    343         }
    344     }
    345     return p_buf;
    346 }
    347 
    348 /*******************************************************************************
    349 **
    350 ** Function         attp_send_msg_to_L2CAP
    351 **
    352 ** Description      Send message to L2CAP.
    353 **
    354 *******************************************************************************/
    355 BOOLEAN  attp_send_msg_to_L2CAP(tGATT_TCB *p_tcb, BT_HDR *p_toL2CAP)
    356 {
    357     UINT16      l2cap_ret;
    358 
    359 
    360     if (p_tcb->att_lcid == L2CAP_ATT_CID)
    361         l2cap_ret = L2CA_SendFixedChnlData (L2CAP_ATT_CID, p_tcb->peer_bda, p_toL2CAP);
    362     else
    363         l2cap_ret = (UINT16) L2CA_DataWrite (p_tcb->att_lcid, p_toL2CAP);
    364 
    365     if (l2cap_ret == L2CAP_DW_FAILED)
    366     {
    367         GATT_TRACE_ERROR1("ATT   failed to pass msg:0x%0x to L2CAP",
    368             *((UINT8 *)(p_toL2CAP + 1) + p_toL2CAP->offset));
    369         GKI_freebuf(p_toL2CAP);
    370         return FALSE;
    371     }
    372     else
    373     {
    374         return TRUE;
    375     }
    376 }
    377 
    378 /*******************************************************************************
    379 **
    380 ** Function         attp_build_sr_msg
    381 **
    382 ** Description      Build ATT Server PDUs.
    383 **
    384 *******************************************************************************/
    385 BT_HDR *attp_build_sr_msg(tGATT_TCB *p_tcb, UINT8 op_code, tGATT_SR_MSG *p_msg)
    386 {
    387     BT_HDR          *p_cmd = NULL;
    388     UINT16          offset = 0;
    389 
    390     switch (op_code)
    391     {
    392     case GATT_RSP_READ_BLOB:
    393     case GATT_RSP_PREPARE_WRITE:
    394         GATT_TRACE_EVENT2 ("ATT_RSP_READ_BLOB/GATT_RSP_PREPARE_WRITE: len = %d offset = %d",
    395                     p_msg->attr_value.len, p_msg->attr_value.offset);
    396         offset = p_msg->attr_value.offset;
    397 /* Coverity: [FALSE-POSITIVE error] intended fall through */
    398 /* Missing break statement between cases in switch statement */
    399         /* fall through */
    400     case GATT_RSP_READ_BY_TYPE:
    401     case GATT_RSP_READ:
    402     case GATT_HANDLE_VALUE_NOTIF:
    403     case GATT_HANDLE_VALUE_IND:
    404         p_cmd = attp_build_value_cmd(p_tcb->payload_size,
    405                                      op_code,
    406                                      p_msg->attr_value.handle,
    407                                      offset,
    408                                      p_msg->attr_value.len,
    409                                      p_msg->attr_value.value);
    410         break;
    411 
    412     case GATT_RSP_WRITE:
    413         p_cmd = attp_build_opcode_cmd(op_code);
    414         break;
    415 
    416     case GATT_RSP_ERROR:
    417         p_cmd = attp_build_err_cmd(p_msg->error.cmd_code, p_msg->error.handle, p_msg->error.reason);
    418         break;
    419 
    420     case GATT_RSP_EXEC_WRITE:
    421         p_cmd = attp_build_exec_write_cmd(op_code, 0);
    422         break;
    423 
    424     case GATT_RSP_MTU:
    425         p_cmd = attp_build_mtu_cmd(op_code, p_msg->mtu);
    426         break;
    427 
    428     default:
    429         GATT_TRACE_DEBUG1("attp_build_sr_msg: unknown op code = %d", op_code);
    430         break;
    431     }
    432 
    433     if (!p_cmd)
    434         GATT_TRACE_ERROR0("No resources");
    435 
    436     return p_cmd;
    437 }
    438 
    439 /*******************************************************************************
    440 **
    441 ** Function         attp_send_sr_msg
    442 **
    443 ** Description      This function sends the server response or indication message
    444 **                  to client.
    445 **
    446 ** Parameter        p_tcb: pointer to the connecton control block.
    447 **                  p_msg: pointer to message parameters structure.
    448 **
    449 ** Returns          GATT_SUCCESS if sucessfully sent; otherwise error code.
    450 **
    451 **
    452 *******************************************************************************/
    453 tGATT_STATUS attp_send_sr_msg (tGATT_TCB *p_tcb, BT_HDR *p_msg)
    454 {
    455     tGATT_STATUS     cmd_sent = GATT_NO_RESOURCES;
    456 
    457     if (p_tcb != NULL)
    458     {
    459         if (p_msg != NULL)
    460         {
    461             p_msg->offset = L2CAP_MIN_OFFSET;
    462 
    463             if (attp_send_msg_to_L2CAP (p_tcb, p_msg))
    464                 cmd_sent = GATT_SUCCESS;
    465             else
    466                 cmd_sent = GATT_INTERNAL_ERROR;
    467         }
    468     }
    469     return cmd_sent;
    470 }
    471 
    472 /*******************************************************************************
    473 **
    474 ** Function         attp_cl_send_cmd
    475 **
    476 ** Description      Send a ATT command or enqueue it.
    477 **
    478 ** Returns          TRUE if command sent, otherwise FALSE.
    479 **
    480 *******************************************************************************/
    481 UINT8 attp_cl_send_cmd(tGATT_TCB *p_tcb, UINT16 clcb_idx, UINT8 cmd_code, BT_HDR *p_cmd)
    482 {
    483     UINT8       att_ret = GATT_SUCCESS;
    484 
    485     if (p_tcb != NULL)
    486     {
    487         cmd_code &= ~GATT_AUTH_SIGN_MASK;
    488 
    489         if (p_tcb->pending_cl_req == p_tcb->next_slot_inq ||
    490             cmd_code == GATT_HANDLE_VALUE_CONF)
    491         {
    492             /* no penindg request or value confirmation */
    493             if (attp_send_msg_to_L2CAP(p_tcb, p_cmd))
    494             {
    495                 /* do not enq cmd if handle value confirmation or set request */
    496                 if (cmd_code != GATT_HANDLE_VALUE_CONF && cmd_code != GATT_CMD_WRITE)
    497                 {
    498                     gatt_start_rsp_timer (p_tcb);
    499                     gatt_cmd_enq(p_tcb, clcb_idx, FALSE, cmd_code, NULL);
    500                 }
    501             }
    502             else
    503                 att_ret = GATT_INTERNAL_ERROR;
    504         }
    505         else
    506         {
    507             att_ret = GATT_CMD_STARTED;
    508             gatt_cmd_enq(p_tcb, clcb_idx, TRUE, cmd_code, p_cmd);
    509         }
    510     }
    511     else
    512         att_ret = GATT_ILLEGAL_PARAMETER;
    513 
    514     return att_ret;
    515 }
    516 /*******************************************************************************
    517 **
    518 ** Function         attp_send_cl_msg
    519 **
    520 ** Description      This function sends the client request or confirmation message
    521 **                  to server.
    522 **
    523 ** Parameter        p_tcb: pointer to the connectino control block.
    524 **                  clcb_idx: clcb index
    525 **                  op_code: message op code.
    526 **                  p_msg: pointer to message parameters structure.
    527 **
    528 ** Returns          GATT_SUCCESS if sucessfully sent; otherwise error code.
    529 **
    530 **
    531 *******************************************************************************/
    532 tGATT_STATUS attp_send_cl_msg (tGATT_TCB *p_tcb, UINT16 clcb_idx, UINT8 op_code, tGATT_CL_MSG *p_msg)
    533 {
    534     tGATT_STATUS     status = GATT_NO_RESOURCES;
    535     BT_HDR          *p_cmd = NULL;
    536     UINT16          offset = 0, handle;
    537 
    538     if (p_tcb != NULL)
    539     {
    540         switch (op_code)
    541         {
    542         case GATT_REQ_MTU:
    543             if (p_msg->mtu <= GATT_MAX_MTU_SIZE)
    544             {
    545                 p_tcb->payload_size = p_msg->mtu;
    546                 p_cmd = attp_build_mtu_cmd(GATT_REQ_MTU, p_msg->mtu);
    547             }
    548             else
    549                 status = GATT_ILLEGAL_PARAMETER;
    550             break;
    551 
    552         case GATT_REQ_FIND_INFO:
    553         case GATT_REQ_READ_BY_TYPE:
    554         case GATT_REQ_READ_BY_GRP_TYPE:
    555             if (GATT_HANDLE_IS_VALID (p_msg->browse.s_handle) &&
    556                 GATT_HANDLE_IS_VALID (p_msg->browse.e_handle)  &&
    557                 p_msg->browse.s_handle <= p_msg->browse.e_handle)
    558             {
    559                 p_cmd = attp_build_browse_cmd(op_code,
    560                                             p_msg->browse.s_handle,
    561                                             p_msg->browse.e_handle,
    562                                             p_msg->browse.uuid);
    563             }
    564             else
    565                 status = GATT_ILLEGAL_PARAMETER;
    566             break;
    567 
    568         case GATT_REQ_READ_BLOB:
    569             offset = p_msg->read_blob.offset;
    570             /* fall through */
    571         case GATT_REQ_READ:
    572             handle = (op_code == GATT_REQ_READ) ? p_msg->handle: p_msg->read_blob.handle;
    573             /*  handle checking */
    574             if (GATT_HANDLE_IS_VALID (handle))
    575             {
    576                 p_cmd = attp_build_handle_cmd(op_code, handle, offset);
    577             }
    578             else
    579                 status = GATT_ILLEGAL_PARAMETER;
    580             break;
    581 
    582         case GATT_HANDLE_VALUE_CONF:
    583             p_cmd = attp_build_opcode_cmd(op_code);
    584             break;
    585 
    586         case GATT_REQ_PREPARE_WRITE:
    587             offset = p_msg->attr_value.offset;
    588             /* fall through */
    589         case GATT_REQ_WRITE:
    590         case GATT_CMD_WRITE:
    591         case GATT_SIGN_CMD_WRITE:
    592             if (GATT_HANDLE_IS_VALID (p_msg->attr_value.handle))
    593             {
    594                 p_cmd = attp_build_value_cmd (p_tcb->payload_size,
    595                                               op_code, p_msg->attr_value.handle,
    596                                               offset,
    597                                               p_msg->attr_value.len,
    598                                               p_msg->attr_value.value);
    599             }
    600             else
    601                 status = GATT_ILLEGAL_PARAMETER;
    602             break;
    603 
    604         case GATT_REQ_EXEC_WRITE:
    605             p_cmd = attp_build_exec_write_cmd(op_code, p_msg->exec_write);
    606             break;
    607 
    608         case GATT_REQ_FIND_TYPE_VALUE:
    609             p_cmd = attp_build_read_handles_cmd(p_tcb->payload_size, &p_msg->find_type_value);
    610             break;
    611 
    612         case GATT_REQ_READ_MULTI:
    613             p_cmd = attp_build_read_multi_cmd(p_tcb->payload_size,
    614                                               p_msg->read_multi.num_handles,
    615                                               p_msg->read_multi.handles);
    616             break;
    617 
    618         default:
    619             break;
    620         }
    621 
    622         if (p_cmd != NULL)
    623             status = attp_cl_send_cmd(p_tcb, clcb_idx, op_code, p_cmd);
    624 
    625     }
    626     else
    627     {
    628         GATT_TRACE_ERROR0("Peer device not connected");
    629     }
    630 
    631     return status;
    632 }
    633 #endif
    634