Home | History | Annotate | Download | only in gatt
      1 /******************************************************************************
      2  *
      3  *  Copyright (C) 2008-2014 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_getpoolbuf(GATT_BUF_POOL_ID)) != 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_by_type_value_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_WARNING("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 tGATT_STATUS 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_ERROR("ATT   failed to pass msg:0x%0x to L2CAP",
    368             *((UINT8 *)(p_toL2CAP + 1) + p_toL2CAP->offset));
    369         return GATT_INTERNAL_ERROR;
    370     }
    371     else if (l2cap_ret == L2CAP_DW_CONGESTED)
    372     {
    373         GATT_TRACE_DEBUG("ATT congested, message accepted");
    374         return GATT_CONGESTED;
    375     }
    376     return GATT_SUCCESS;
    377 }
    378 
    379 /*******************************************************************************
    380 **
    381 ** Function         attp_build_sr_msg
    382 **
    383 ** Description      Build ATT Server PDUs.
    384 **
    385 *******************************************************************************/
    386 BT_HDR *attp_build_sr_msg(tGATT_TCB *p_tcb, UINT8 op_code, tGATT_SR_MSG *p_msg)
    387 {
    388     BT_HDR          *p_cmd = NULL;
    389     UINT16          offset = 0;
    390 
    391     switch (op_code)
    392     {
    393     case GATT_RSP_READ_BLOB:
    394     case GATT_RSP_PREPARE_WRITE:
    395         GATT_TRACE_EVENT ("ATT_RSP_READ_BLOB/GATT_RSP_PREPARE_WRITE: len = %d offset = %d",
    396                     p_msg->attr_value.len, p_msg->attr_value.offset);
    397         offset = p_msg->attr_value.offset;
    398 /* Coverity: [FALSE-POSITIVE error] intended fall through */
    399 /* Missing break statement between cases in switch statement */
    400         /* fall through */
    401     case GATT_RSP_READ_BY_TYPE:
    402     case GATT_RSP_READ:
    403     case GATT_HANDLE_VALUE_NOTIF:
    404     case GATT_HANDLE_VALUE_IND:
    405         p_cmd = attp_build_value_cmd(p_tcb->payload_size,
    406                                      op_code,
    407                                      p_msg->attr_value.handle,
    408                                      offset,
    409                                      p_msg->attr_value.len,
    410                                      p_msg->attr_value.value);
    411         break;
    412 
    413     case GATT_RSP_WRITE:
    414         p_cmd = attp_build_opcode_cmd(op_code);
    415         break;
    416 
    417     case GATT_RSP_ERROR:
    418         p_cmd = attp_build_err_cmd(p_msg->error.cmd_code, p_msg->error.handle, p_msg->error.reason);
    419         break;
    420 
    421     case GATT_RSP_EXEC_WRITE:
    422         p_cmd = attp_build_exec_write_cmd(op_code, 0);
    423         break;
    424 
    425     case GATT_RSP_MTU:
    426         p_cmd = attp_build_mtu_cmd(op_code, p_msg->mtu);
    427         break;
    428 
    429     default:
    430         GATT_TRACE_DEBUG("attp_build_sr_msg: unknown op code = %d", op_code);
    431         break;
    432     }
    433 
    434     if (!p_cmd)
    435         GATT_TRACE_ERROR("No resources");
    436 
    437     return p_cmd;
    438 }
    439 
    440 /*******************************************************************************
    441 **
    442 ** Function         attp_send_sr_msg
    443 **
    444 ** Description      This function sends the server response or indication message
    445 **                  to client.
    446 **
    447 ** Parameter        p_tcb: pointer to the connecton control block.
    448 **                  p_msg: pointer to message parameters structure.
    449 **
    450 ** Returns          GATT_SUCCESS if sucessfully sent; otherwise error code.
    451 **
    452 **
    453 *******************************************************************************/
    454 tGATT_STATUS attp_send_sr_msg (tGATT_TCB *p_tcb, BT_HDR *p_msg)
    455 {
    456     tGATT_STATUS     cmd_sent = GATT_NO_RESOURCES;
    457 
    458     if (p_tcb != NULL)
    459     {
    460         if (p_msg != NULL)
    461         {
    462             p_msg->offset = L2CAP_MIN_OFFSET;
    463             cmd_sent = attp_send_msg_to_l2cap (p_tcb, p_msg);
    464         }
    465     }
    466     return cmd_sent;
    467 }
    468 
    469 /*******************************************************************************
    470 **
    471 ** Function         attp_cl_send_cmd
    472 **
    473 ** Description      Send a ATT command or enqueue it.
    474 **
    475 ** Returns          GATT_SUCCESS if command sent
    476 **                  GATT_CONGESTED if command sent but channel congested
    477 **                  GATT_CMD_STARTED if command queue up in GATT
    478 **                  GATT_ERROR if command sending failure
    479 **
    480 *******************************************************************************/
    481 tGATT_STATUS attp_cl_send_cmd(tGATT_TCB *p_tcb, UINT16 clcb_idx, UINT8 cmd_code, BT_HDR *p_cmd)
    482 {
    483     tGATT_STATUS att_ret = GATT_SUCCESS;
    484 
    485     if (p_tcb != NULL)
    486     {
    487         cmd_code &= ~GATT_AUTH_SIGN_MASK;
    488 
    489         /* no pending request or value confirmation */
    490         if (p_tcb->pending_cl_req == p_tcb->next_slot_inq ||
    491             cmd_code == GATT_HANDLE_VALUE_CONF)
    492         {
    493             att_ret = attp_send_msg_to_l2cap(p_tcb, p_cmd);
    494             if (att_ret == GATT_CONGESTED || att_ret == GATT_SUCCESS)
    495             {
    496                 /* do not enq cmd if handle value confirmation or set request */
    497                 if (cmd_code != GATT_HANDLE_VALUE_CONF && cmd_code != GATT_CMD_WRITE)
    498                 {
    499                     gatt_start_rsp_timer (clcb_idx);
    500                     gatt_cmd_enq(p_tcb, clcb_idx, FALSE, cmd_code, NULL);
    501                 }
    502             }
    503             else
    504                 att_ret = GATT_INTERNAL_ERROR;
    505         }
    506         else
    507         {
    508             att_ret = GATT_CMD_STARTED;
    509             gatt_cmd_enq(p_tcb, clcb_idx, TRUE, cmd_code, p_cmd);
    510         }
    511     }
    512     else
    513         att_ret = GATT_ERROR;
    514 
    515     return att_ret;
    516 }
    517 /*******************************************************************************
    518 **
    519 ** Function         attp_send_cl_msg
    520 **
    521 ** Description      This function sends the client request or confirmation message
    522 **                  to server.
    523 **
    524 ** Parameter        p_tcb: pointer to the connectino control block.
    525 **                  clcb_idx: clcb index
    526 **                  op_code: message op code.
    527 **                  p_msg: pointer to message parameters structure.
    528 **
    529 ** Returns          GATT_SUCCESS if sucessfully sent; otherwise error code.
    530 **
    531 **
    532 *******************************************************************************/
    533 tGATT_STATUS attp_send_cl_msg (tGATT_TCB *p_tcb, UINT16 clcb_idx, UINT8 op_code, tGATT_CL_MSG *p_msg)
    534 {
    535     tGATT_STATUS     status = GATT_NO_RESOURCES;
    536     BT_HDR          *p_cmd = NULL;
    537     UINT16          offset = 0, handle;
    538 
    539     if (p_tcb != NULL)
    540     {
    541         switch (op_code)
    542         {
    543         case GATT_REQ_MTU:
    544             if (p_msg->mtu <= GATT_MAX_MTU_SIZE)
    545             {
    546                 p_tcb->payload_size = p_msg->mtu;
    547                 p_cmd = attp_build_mtu_cmd(GATT_REQ_MTU, p_msg->mtu);
    548             }
    549             else
    550                 status = GATT_ILLEGAL_PARAMETER;
    551             break;
    552 
    553         case GATT_REQ_FIND_INFO:
    554         case GATT_REQ_READ_BY_TYPE:
    555         case GATT_REQ_READ_BY_GRP_TYPE:
    556             if (GATT_HANDLE_IS_VALID (p_msg->browse.s_handle) &&
    557                 GATT_HANDLE_IS_VALID (p_msg->browse.e_handle)  &&
    558                 p_msg->browse.s_handle <= p_msg->browse.e_handle)
    559             {
    560                 p_cmd = attp_build_browse_cmd(op_code,
    561                                             p_msg->browse.s_handle,
    562                                             p_msg->browse.e_handle,
    563                                             p_msg->browse.uuid);
    564             }
    565             else
    566                 status = GATT_ILLEGAL_PARAMETER;
    567             break;
    568 
    569         case GATT_REQ_READ_BLOB:
    570             offset = p_msg->read_blob.offset;
    571             /* fall through */
    572         case GATT_REQ_READ:
    573             handle = (op_code == GATT_REQ_READ) ? p_msg->handle: p_msg->read_blob.handle;
    574             /*  handle checking */
    575             if (GATT_HANDLE_IS_VALID (handle))
    576             {
    577                 p_cmd = attp_build_handle_cmd(op_code, handle, offset);
    578             }
    579             else
    580                 status = GATT_ILLEGAL_PARAMETER;
    581             break;
    582 
    583         case GATT_HANDLE_VALUE_CONF:
    584             p_cmd = attp_build_opcode_cmd(op_code);
    585             break;
    586 
    587         case GATT_REQ_PREPARE_WRITE:
    588             offset = p_msg->attr_value.offset;
    589             /* fall through */
    590         case GATT_REQ_WRITE:
    591         case GATT_CMD_WRITE:
    592         case GATT_SIGN_CMD_WRITE:
    593             if (GATT_HANDLE_IS_VALID (p_msg->attr_value.handle))
    594             {
    595                 p_cmd = attp_build_value_cmd (p_tcb->payload_size,
    596                                               op_code, p_msg->attr_value.handle,
    597                                               offset,
    598                                               p_msg->attr_value.len,
    599                                               p_msg->attr_value.value);
    600             }
    601             else
    602                 status = GATT_ILLEGAL_PARAMETER;
    603             break;
    604 
    605         case GATT_REQ_EXEC_WRITE:
    606             p_cmd = attp_build_exec_write_cmd(op_code, p_msg->exec_write);
    607             break;
    608 
    609         case GATT_REQ_FIND_TYPE_VALUE:
    610             p_cmd = attp_build_read_by_type_value_cmd(p_tcb->payload_size, &p_msg->find_type_value);
    611             break;
    612 
    613         case GATT_REQ_READ_MULTI:
    614             p_cmd = attp_build_read_multi_cmd(p_tcb->payload_size,
    615                                               p_msg->read_multi.num_handles,
    616                                               p_msg->read_multi.handles);
    617             break;
    618 
    619         default:
    620             break;
    621         }
    622 
    623         if (p_cmd != NULL)
    624             status = attp_cl_send_cmd(p_tcb, clcb_idx, op_code, p_cmd);
    625 
    626     }
    627     else
    628     {
    629         GATT_TRACE_ERROR("Peer device not connected");
    630     }
    631 
    632     return status;
    633 }
    634 #endif
    635