Home | History | Annotate | Download | only in mcap
      1 /******************************************************************************
      2  *
      3  *  Copyright 2009-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 is the implementation file for the MCAP at L2CAP Interface.
     22  *
     23  ******************************************************************************/
     24 #include <string.h>
     25 
     26 #include "bt_target.h"
     27 #include "bt_utils.h"
     28 #include "btm_api.h"
     29 #include "btm_int.h"
     30 #include "mca_api.h"
     31 #include "mca_defs.h"
     32 #include "mca_int.h"
     33 #include "osi/include/osi.h"
     34 
     35 /* L2CAP callback function structure */
     36 const tL2CAP_APPL_INFO mca_l2c_int_appl = {
     37     NULL,
     38     mca_l2c_connect_cfm_cback,
     39     NULL,
     40     mca_l2c_config_ind_cback,
     41     mca_l2c_config_cfm_cback,
     42     mca_l2c_disconnect_ind_cback,
     43     mca_l2c_disconnect_cfm_cback,
     44     NULL,
     45     mca_l2c_data_ind_cback,
     46     mca_l2c_congestion_ind_cback,
     47     NULL,
     48     NULL /* tL2CA_CREDITS_RECEIVED_CB */};
     49 
     50 /* Control channel eL2CAP default options */
     51 const tL2CAP_FCR_OPTS mca_l2c_fcr_opts_def = {
     52     L2CAP_FCR_ERTM_MODE,          /* Mandatory for MCAP */
     53     MCA_FCR_OPT_TX_WINDOW_SIZE,   /* Tx window size */
     54     MCA_FCR_OPT_MAX_TX_B4_DISCNT, /* Maximum transmissions before
     55                                      disconnecting */
     56     MCA_FCR_OPT_RETX_TOUT,        /* Retransmission timeout (2 secs) */
     57     MCA_FCR_OPT_MONITOR_TOUT,     /* Monitor timeout (12 secs) */
     58     MCA_FCR_OPT_MPS_SIZE          /* MPS segment size */
     59 };
     60 
     61 /*******************************************************************************
     62  *
     63  * Function         mca_sec_check_complete_term
     64  *
     65  * Description      The function called when Security Manager finishes
     66  *                  verification of the service side connection
     67  *
     68  * Returns          void
     69  *
     70  ******************************************************************************/
     71 static void mca_sec_check_complete_term(const RawAddress* bd_addr,
     72                                         UNUSED_ATTR tBT_TRANSPORT transport,
     73                                         void* p_ref_data, uint8_t res) {
     74   tMCA_TC_TBL* p_tbl = (tMCA_TC_TBL*)p_ref_data;
     75   tL2CAP_CFG_INFO cfg;
     76   tL2CAP_ERTM_INFO ertm_info;
     77 
     78   MCA_TRACE_DEBUG("mca_sec_check_complete_term res: %d", res);
     79 
     80   if (res == BTM_SUCCESS) {
     81     MCA_TRACE_DEBUG("lcid:x%x id:x%x", p_tbl->lcid, p_tbl->id);
     82     /* Set the FCR options: control channel mandates ERTM */
     83     ertm_info.preferred_mode = mca_l2c_fcr_opts_def.mode;
     84     ertm_info.allowed_modes = L2CAP_FCR_CHAN_OPT_ERTM;
     85     ertm_info.user_rx_buf_size = MCA_USER_RX_BUF_SIZE;
     86     ertm_info.user_tx_buf_size = MCA_USER_TX_BUF_SIZE;
     87     ertm_info.fcr_rx_buf_size = MCA_FCR_RX_BUF_SIZE;
     88     ertm_info.fcr_tx_buf_size = MCA_FCR_TX_BUF_SIZE;
     89     /* Send response to the L2CAP layer. */
     90     L2CA_ErtmConnectRsp(*bd_addr, p_tbl->id, p_tbl->lcid, L2CAP_CONN_OK,
     91                         L2CAP_CONN_OK, &ertm_info);
     92 
     93     /* transition to configuration state */
     94     p_tbl->state = MCA_TC_ST_CFG;
     95 
     96     /* Send L2CAP config req */
     97     mca_set_cfg_by_tbl(&cfg, p_tbl);
     98     L2CA_ConfigReq(p_tbl->lcid, &cfg);
     99   } else {
    100     L2CA_ConnectRsp(*bd_addr, p_tbl->id, p_tbl->lcid, L2CAP_CONN_SECURITY_BLOCK,
    101                     L2CAP_CONN_OK);
    102     mca_tc_close_ind(p_tbl, L2CAP_CONN_SECURITY_BLOCK);
    103   }
    104 }
    105 
    106 /*******************************************************************************
    107  *
    108  * Function         mca_sec_check_complete_orig
    109  *
    110  * Description      The function called when Security Manager finishes
    111  *                  verification of the service side connection
    112  *
    113  * Returns          void
    114  *
    115  ******************************************************************************/
    116 static void mca_sec_check_complete_orig(UNUSED_ATTR const RawAddress* bd_addr,
    117                                         UNUSED_ATTR tBT_TRANSPORT transport,
    118                                         void* p_ref_data, uint8_t res) {
    119   tMCA_TC_TBL* p_tbl = (tMCA_TC_TBL*)p_ref_data;
    120   tL2CAP_CFG_INFO cfg;
    121 
    122   MCA_TRACE_DEBUG("mca_sec_check_complete_orig res: %d", res);
    123 
    124   if (res == BTM_SUCCESS) {
    125     /* set channel state */
    126     p_tbl->state = MCA_TC_ST_CFG;
    127 
    128     /* Send L2CAP config req */
    129     mca_set_cfg_by_tbl(&cfg, p_tbl);
    130     L2CA_ConfigReq(p_tbl->lcid, &cfg);
    131   } else {
    132     L2CA_DisconnectReq(p_tbl->lcid);
    133     mca_tc_close_ind(p_tbl, L2CAP_CONN_SECURITY_BLOCK);
    134   }
    135 }
    136 /*******************************************************************************
    137  *
    138  * Function         mca_l2c_cconn_ind_cback
    139  *
    140  * Description      This is the L2CAP connect indication callback function.
    141  *
    142  * Returns          void
    143  *
    144  ******************************************************************************/
    145 void mca_l2c_cconn_ind_cback(const RawAddress& bd_addr, uint16_t lcid,
    146                              uint16_t psm, uint8_t id) {
    147   tMCA_HANDLE handle = mca_handle_by_cpsm(psm);
    148   tMCA_CCB* p_ccb;
    149   tMCA_TC_TBL* p_tbl = NULL;
    150   uint16_t result = L2CAP_CONN_NO_RESOURCES;
    151   tBTM_STATUS rc;
    152   tL2CAP_ERTM_INFO ertm_info, *p_ertm_info = NULL;
    153   tL2CAP_CFG_INFO cfg;
    154 
    155   MCA_TRACE_EVENT("mca_l2c_cconn_ind_cback: lcid:x%x psm:x%x id:x%x", lcid, psm,
    156                   id);
    157 
    158   /* do we already have a control channel for this peer? */
    159   p_ccb = mca_ccb_by_bd(handle, bd_addr);
    160   if (p_ccb == NULL) {
    161     /* no, allocate ccb */
    162     p_ccb = mca_ccb_alloc(handle, bd_addr);
    163     if (p_ccb != NULL) {
    164       /* allocate and set up entry */
    165       p_ccb->lcid = lcid;
    166       p_tbl = mca_tc_tbl_calloc(p_ccb);
    167       p_tbl->id = id;
    168       p_tbl->cfg_flags = MCA_L2C_CFG_CONN_ACP;
    169       /* proceed with connection */
    170       /* Check the security */
    171       rc = btm_sec_mx_access_request(bd_addr, psm, false, BTM_SEC_PROTO_MCA, 0,
    172                                      &mca_sec_check_complete_term, p_tbl);
    173       if (rc == BTM_CMD_STARTED) {
    174         /* Set the FCR options: control channel mandates ERTM */
    175         ertm_info.preferred_mode = mca_l2c_fcr_opts_def.mode;
    176         ertm_info.allowed_modes = L2CAP_FCR_CHAN_OPT_ERTM;
    177         ertm_info.user_rx_buf_size = MCA_USER_RX_BUF_SIZE;
    178         ertm_info.user_tx_buf_size = MCA_USER_TX_BUF_SIZE;
    179         ertm_info.fcr_rx_buf_size = MCA_FCR_RX_BUF_SIZE;
    180         ertm_info.fcr_tx_buf_size = MCA_FCR_TX_BUF_SIZE;
    181         p_ertm_info = &ertm_info;
    182         result = L2CAP_CONN_PENDING;
    183       } else
    184         result = L2CAP_CONN_OK;
    185     }
    186 
    187     /*  deal with simultaneous control channel connect case */
    188   }
    189   /* else reject their connection */
    190 
    191   if (!p_tbl || (p_tbl->state != MCA_TC_ST_CFG)) {
    192     /* Send L2CAP connect rsp */
    193     L2CA_ErtmConnectRsp(bd_addr, id, lcid, result, L2CAP_CONN_OK, p_ertm_info);
    194 
    195     /* if result ok, proceed with connection and send L2CAP
    196        config req */
    197     if (result == L2CAP_CONN_OK) {
    198       /* set channel state */
    199       p_tbl->state = MCA_TC_ST_CFG;
    200 
    201       /* Send L2CAP config req */
    202       mca_set_cfg_by_tbl(&cfg, p_tbl);
    203       L2CA_ConfigReq(p_tbl->lcid, &cfg);
    204     }
    205   }
    206 }
    207 
    208 /*******************************************************************************
    209  *
    210  * Function         mca_l2c_dconn_ind_cback
    211  *
    212  * Description      This is the L2CAP connect indication callback function.
    213  *
    214  *
    215  * Returns          void
    216  *
    217  ******************************************************************************/
    218 void mca_l2c_dconn_ind_cback(const RawAddress& bd_addr, uint16_t lcid,
    219                              uint16_t psm, uint8_t id) {
    220   tMCA_HANDLE handle = mca_handle_by_dpsm(psm);
    221   tMCA_CCB* p_ccb;
    222   tMCA_DCB* p_dcb;
    223   tMCA_TC_TBL* p_tbl = NULL;
    224   uint16_t result;
    225   tL2CAP_CFG_INFO cfg;
    226   tL2CAP_ERTM_INFO *p_ertm_info = NULL, ertm_info;
    227   const tMCA_CHNL_CFG* p_chnl_cfg;
    228 
    229   MCA_TRACE_EVENT("mca_l2c_dconn_ind_cback: lcid:x%x psm:x%x ", lcid, psm);
    230 
    231   if (((p_ccb = mca_ccb_by_bd(handle, bd_addr)) != NULL) && /* find the CCB */
    232       (p_ccb->status ==
    233        MCA_CCB_STAT_PENDING) && /* this CCB is expecting a MDL */
    234       (p_ccb->p_tx_req &&
    235        (p_dcb = mca_dcb_by_hdl(p_ccb->p_tx_req->dcb_idx)) != NULL)) {
    236     /* found the associated dcb in listening mode */
    237     /* proceed with connection */
    238     p_dcb->lcid = lcid;
    239     p_tbl = mca_tc_tbl_dalloc(p_dcb);
    240     p_tbl->id = id;
    241     p_tbl->cfg_flags = MCA_L2C_CFG_CONN_ACP;
    242     p_chnl_cfg = p_dcb->p_chnl_cfg;
    243     /* assume that control channel has verified the security requirement */
    244     /* Set the FCR options: control channel mandates ERTM */
    245     ertm_info.preferred_mode = p_chnl_cfg->fcr_opt.mode;
    246     ertm_info.allowed_modes = (1 << p_chnl_cfg->fcr_opt.mode);
    247     ertm_info.user_rx_buf_size = p_chnl_cfg->user_rx_buf_size;
    248     ertm_info.user_tx_buf_size = p_chnl_cfg->user_tx_buf_size;
    249     ertm_info.fcr_rx_buf_size = p_chnl_cfg->fcr_rx_buf_size;
    250     ertm_info.fcr_tx_buf_size = p_chnl_cfg->fcr_tx_buf_size;
    251     p_ertm_info = &ertm_info;
    252     result = L2CAP_CONN_OK;
    253   } else {
    254     /* else we're not listening for traffic channel; reject
    255      * (this error code is specified by MCAP spec) */
    256     result = L2CAP_CONN_NO_RESOURCES;
    257   }
    258 
    259   /* Send L2CAP connect rsp */
    260   L2CA_ErtmConnectRsp(bd_addr, id, lcid, result, result, p_ertm_info);
    261 
    262   /* if result ok, proceed with connection */
    263   if (result == L2CAP_CONN_OK) {
    264     /* transition to configuration state */
    265     p_tbl->state = MCA_TC_ST_CFG;
    266 
    267     /* Send L2CAP config req */
    268     mca_set_cfg_by_tbl(&cfg, p_tbl);
    269     L2CA_ConfigReq(lcid, &cfg);
    270   }
    271 }
    272 
    273 /*******************************************************************************
    274  *
    275  * Function         mca_l2c_connect_cfm_cback
    276  *
    277  * Description      This is the L2CAP connect confirm callback function.
    278  *
    279  *
    280  * Returns          void
    281  *
    282  ******************************************************************************/
    283 void mca_l2c_connect_cfm_cback(uint16_t lcid, uint16_t result) {
    284   tMCA_TC_TBL* p_tbl;
    285   tL2CAP_CFG_INFO cfg;
    286   tMCA_CCB* p_ccb;
    287 
    288   MCA_TRACE_DEBUG("mca_l2c_connect_cfm_cback lcid: x%x, result: %d", lcid,
    289                   result);
    290   /* look up info for this channel */
    291   p_tbl = mca_tc_tbl_by_lcid(lcid);
    292   if (p_tbl != NULL) {
    293     MCA_TRACE_DEBUG("p_tbl state: %d, tcid: %d", p_tbl->state, p_tbl->tcid);
    294     /* if in correct state */
    295     if (p_tbl->state == MCA_TC_ST_CONN) {
    296       /* if result successful */
    297       if (result == L2CAP_CONN_OK) {
    298         if (p_tbl->tcid != 0) {
    299           /* set channel state */
    300           p_tbl->state = MCA_TC_ST_CFG;
    301 
    302           /* Send L2CAP config req */
    303           mca_set_cfg_by_tbl(&cfg, p_tbl);
    304           L2CA_ConfigReq(lcid, &cfg);
    305         } else {
    306           p_ccb = mca_ccb_by_hdl((tMCA_CL)p_tbl->cb_idx);
    307           if (p_ccb == NULL) {
    308             result = L2CAP_CONN_NO_RESOURCES;
    309           } else {
    310             /* set channel state */
    311             p_tbl->state = MCA_TC_ST_SEC_INT;
    312             p_tbl->lcid = lcid;
    313             p_tbl->cfg_flags = MCA_L2C_CFG_CONN_INT;
    314 
    315             /* Check the security */
    316             btm_sec_mx_access_request(p_ccb->peer_addr, p_ccb->ctrl_vpsm, true,
    317                                       BTM_SEC_PROTO_MCA, p_tbl->tcid,
    318                                       &mca_sec_check_complete_orig, p_tbl);
    319           }
    320         }
    321       }
    322 
    323       /* failure; notify adaption that channel closed */
    324       if (result != L2CAP_CONN_OK) {
    325         p_tbl->cfg_flags |= MCA_L2C_CFG_DISCN_INT;
    326         mca_tc_close_ind(p_tbl, result);
    327       }
    328     }
    329   }
    330 }
    331 
    332 /*******************************************************************************
    333  *
    334  * Function         mca_l2c_config_cfm_cback
    335  *
    336  * Description      This is the L2CAP config confirm callback function.
    337  *
    338  *
    339  * Returns          void
    340  *
    341  ******************************************************************************/
    342 void mca_l2c_config_cfm_cback(uint16_t lcid, tL2CAP_CFG_INFO* p_cfg) {
    343   tMCA_TC_TBL* p_tbl;
    344 
    345   /* look up info for this channel */
    346   p_tbl = mca_tc_tbl_by_lcid(lcid);
    347   if (p_tbl != NULL) {
    348     /* if in correct state */
    349     if (p_tbl->state == MCA_TC_ST_CFG) {
    350       /* if result successful */
    351       if (p_cfg->result == L2CAP_CONN_OK) {
    352         /* update cfg_flags */
    353         p_tbl->cfg_flags |= MCA_L2C_CFG_CFM_DONE;
    354 
    355         /* if configuration complete */
    356         if (p_tbl->cfg_flags & MCA_L2C_CFG_IND_DONE) {
    357           mca_tc_open_ind(p_tbl);
    358         }
    359       }
    360       /* else failure */
    361       else {
    362         /* Send L2CAP disconnect req */
    363         L2CA_DisconnectReq(lcid);
    364       }
    365     }
    366   }
    367 }
    368 
    369 /*******************************************************************************
    370  *
    371  * Function         mca_l2c_config_ind_cback
    372  *
    373  * Description      This is the L2CAP config indication callback function.
    374  *
    375  *
    376  * Returns          void
    377  *
    378  ******************************************************************************/
    379 void mca_l2c_config_ind_cback(uint16_t lcid, tL2CAP_CFG_INFO* p_cfg) {
    380   tMCA_TC_TBL* p_tbl;
    381   uint16_t result = L2CAP_CFG_OK;
    382 
    383   /* look up info for this channel */
    384   p_tbl = mca_tc_tbl_by_lcid(lcid);
    385   if (p_tbl != NULL) {
    386     /* store the mtu in tbl */
    387     if (p_cfg->mtu_present) {
    388       p_tbl->peer_mtu = p_cfg->mtu;
    389       if (p_tbl->peer_mtu < MCA_MIN_MTU) {
    390         result = L2CAP_CFG_UNACCEPTABLE_PARAMS;
    391       }
    392     } else {
    393       p_tbl->peer_mtu = L2CAP_DEFAULT_MTU;
    394     }
    395     MCA_TRACE_DEBUG("peer_mtu: %d, lcid: x%x mtu_present:%d", p_tbl->peer_mtu,
    396                     lcid, p_cfg->mtu_present);
    397 
    398     /* send L2CAP configure response */
    399     memset(p_cfg, 0, sizeof(tL2CAP_CFG_INFO));
    400     p_cfg->result = result;
    401     L2CA_ConfigRsp(lcid, p_cfg);
    402 
    403     /* if first config ind */
    404     if ((p_tbl->cfg_flags & MCA_L2C_CFG_IND_DONE) == 0) {
    405       /* update cfg_flags */
    406       p_tbl->cfg_flags |= MCA_L2C_CFG_IND_DONE;
    407 
    408       /* if configuration complete */
    409       if (p_tbl->cfg_flags & MCA_L2C_CFG_CFM_DONE) {
    410         mca_tc_open_ind(p_tbl);
    411       }
    412     }
    413   }
    414 }
    415 
    416 /*******************************************************************************
    417  *
    418  * Function         mca_l2c_disconnect_ind_cback
    419  *
    420  * Description      This is the L2CAP disconnect indication callback function.
    421  *
    422  *
    423  * Returns          void
    424  *
    425  ******************************************************************************/
    426 void mca_l2c_disconnect_ind_cback(uint16_t lcid, bool ack_needed) {
    427   tMCA_TC_TBL* p_tbl;
    428   uint16_t reason = L2CAP_DISC_TIMEOUT;
    429 
    430   MCA_TRACE_DEBUG("mca_l2c_disconnect_ind_cback lcid: %d, ack_needed: %d", lcid,
    431                   ack_needed);
    432   /* look up info for this channel */
    433   p_tbl = mca_tc_tbl_by_lcid(lcid);
    434   if (p_tbl != NULL) {
    435     if (ack_needed) {
    436       /* send L2CAP disconnect response */
    437       L2CA_DisconnectRsp(lcid);
    438     }
    439 
    440     p_tbl->cfg_flags = MCA_L2C_CFG_DISCN_ACP;
    441     if (ack_needed) reason = L2CAP_DISC_OK;
    442     mca_tc_close_ind(p_tbl, reason);
    443   }
    444 }
    445 
    446 /*******************************************************************************
    447  *
    448  * Function         mca_l2c_disconnect_cfm_cback
    449  *
    450  * Description      This is the L2CAP disconnect confirm callback function.
    451  *
    452  *
    453  * Returns          void
    454  *
    455  ******************************************************************************/
    456 void mca_l2c_disconnect_cfm_cback(uint16_t lcid, uint16_t result) {
    457   tMCA_TC_TBL* p_tbl;
    458 
    459   MCA_TRACE_DEBUG("mca_l2c_disconnect_cfm_cback lcid: x%x, result: %d", lcid,
    460                   result);
    461   /* look up info for this channel */
    462   p_tbl = mca_tc_tbl_by_lcid(lcid);
    463   if (p_tbl != NULL) {
    464     p_tbl->cfg_flags = MCA_L2C_CFG_DISCN_INT;
    465     mca_tc_close_ind(p_tbl, result);
    466   }
    467 }
    468 
    469 /*******************************************************************************
    470  *
    471  * Function         mca_l2c_congestion_ind_cback
    472  *
    473  * Description      This is the L2CAP congestion indication callback function.
    474  *
    475  *
    476  * Returns          void
    477  *
    478  ******************************************************************************/
    479 void mca_l2c_congestion_ind_cback(uint16_t lcid, bool is_congested) {
    480   tMCA_TC_TBL* p_tbl;
    481 
    482   /* look up info for this channel */
    483   p_tbl = mca_tc_tbl_by_lcid(lcid);
    484   if (p_tbl != NULL) {
    485     mca_tc_cong_ind(p_tbl, is_congested);
    486   }
    487 }
    488 
    489 /*******************************************************************************
    490  *
    491  * Function         mca_l2c_data_ind_cback
    492  *
    493  * Description      This is the L2CAP data indication callback function.
    494  *
    495  *
    496  * Returns          void
    497  *
    498  ******************************************************************************/
    499 void mca_l2c_data_ind_cback(uint16_t lcid, BT_HDR* p_buf) {
    500   tMCA_TC_TBL* p_tbl;
    501 
    502   /* look up info for this channel */
    503   p_tbl = mca_tc_tbl_by_lcid(lcid);
    504   if (p_tbl != NULL) {
    505     mca_tc_data_ind(p_tbl, p_buf);
    506   } else /* prevent buffer leak */
    507     osi_free(p_buf);
    508 }
    509 
    510 /*******************************************************************************
    511  *
    512  * Function         mca_l2c_open_req
    513  *
    514  * Description      This function calls L2CA_ConnectReq() to initiate a L2CAP
    515  *                  channel.
    516  *
    517  * Returns          void.
    518  *
    519  ******************************************************************************/
    520 uint16_t mca_l2c_open_req(const RawAddress& bd_addr, uint16_t psm,
    521                           const tMCA_CHNL_CFG* p_chnl_cfg) {
    522   tL2CAP_ERTM_INFO ertm_info;
    523 
    524   if (p_chnl_cfg) {
    525     ertm_info.preferred_mode = p_chnl_cfg->fcr_opt.mode;
    526     ertm_info.allowed_modes = (1 << p_chnl_cfg->fcr_opt.mode);
    527     ertm_info.user_rx_buf_size = p_chnl_cfg->user_rx_buf_size;
    528     ertm_info.user_tx_buf_size = p_chnl_cfg->user_tx_buf_size;
    529     ertm_info.fcr_rx_buf_size = p_chnl_cfg->fcr_rx_buf_size;
    530     ertm_info.fcr_tx_buf_size = p_chnl_cfg->fcr_tx_buf_size;
    531   } else {
    532     ertm_info.preferred_mode = mca_l2c_fcr_opts_def.mode;
    533     ertm_info.allowed_modes = L2CAP_FCR_CHAN_OPT_ERTM;
    534     ertm_info.user_rx_buf_size = MCA_USER_RX_BUF_SIZE;
    535     ertm_info.user_tx_buf_size = MCA_USER_TX_BUF_SIZE;
    536     ertm_info.fcr_rx_buf_size = MCA_FCR_RX_BUF_SIZE;
    537     ertm_info.fcr_tx_buf_size = MCA_FCR_TX_BUF_SIZE;
    538   }
    539   return L2CA_ErtmConnectReq(psm, bd_addr, &ertm_info);
    540 }
    541