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