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