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 *******************************************************************************/
mca_sec_check_complete_term(BD_ADDR bd_addr,tBT_TRANSPORT transport,void * p_ref_data,UINT8 res)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_buf_size  = MCA_USER_RX_BUF_SIZE;
90         ertm_info.user_tx_buf_size  = MCA_USER_TX_BUF_SIZE;
91         ertm_info.fcr_rx_buf_size   = MCA_FCR_RX_BUF_SIZE;
92         ertm_info.fcr_tx_buf_size   = MCA_FCR_TX_BUF_SIZE;
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 *******************************************************************************/
mca_sec_check_complete_orig(BD_ADDR bd_addr,tBT_TRANSPORT transport,void * p_ref_data,UINT8 res)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 *******************************************************************************/
mca_l2c_cconn_ind_cback(BD_ADDR bd_addr,UINT16 lcid,UINT16 psm,UINT8 id)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_buf_size  = MCA_USER_RX_BUF_SIZE;
186                 ertm_info.user_tx_buf_size  = MCA_USER_TX_BUF_SIZE;
187                 ertm_info.fcr_rx_buf_size   = MCA_FCR_RX_BUF_SIZE;
188                 ertm_info.fcr_tx_buf_size   = MCA_FCR_TX_BUF_SIZE;
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 *******************************************************************************/
mca_l2c_dconn_ind_cback(BD_ADDR bd_addr,UINT16 lcid,UINT16 psm,UINT8 id)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_buf_size  = p_chnl_cfg->user_rx_buf_size;
258         ertm_info.user_tx_buf_size  = p_chnl_cfg->user_tx_buf_size;
259         ertm_info.fcr_rx_buf_size   = p_chnl_cfg->fcr_rx_buf_size;
260         ertm_info.fcr_tx_buf_size   = p_chnl_cfg->fcr_tx_buf_size;
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 *******************************************************************************/
mca_l2c_connect_cfm_cback(UINT16 lcid,UINT16 result)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 *******************************************************************************/
mca_l2c_config_cfm_cback(UINT16 lcid,tL2CAP_CFG_INFO * p_cfg)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 *******************************************************************************/
mca_l2c_config_ind_cback(UINT16 lcid,tL2CAP_CFG_INFO * p_cfg)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 *******************************************************************************/
mca_l2c_disconnect_ind_cback(UINT16 lcid,BOOLEAN ack_needed)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 *******************************************************************************/
mca_l2c_disconnect_cfm_cback(UINT16 lcid,UINT16 result)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 *******************************************************************************/
mca_l2c_congestion_ind_cback(UINT16 lcid,BOOLEAN is_congested)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 *******************************************************************************/
mca_l2c_data_ind_cback(UINT16 lcid,BT_HDR * p_buf)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         osi_free(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 *******************************************************************************/
mca_l2c_open_req(BD_ADDR bd_addr,UINT16 psm,const tMCA_CHNL_CFG * p_chnl_cfg)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_buf_size  = p_chnl_cfg->user_rx_buf_size;
572         ertm_info.user_tx_buf_size  = p_chnl_cfg->user_tx_buf_size;
573         ertm_info.fcr_rx_buf_size   = p_chnl_cfg->fcr_rx_buf_size;
574         ertm_info.fcr_tx_buf_size   = p_chnl_cfg->fcr_tx_buf_size;
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_buf_size  = MCA_USER_RX_BUF_SIZE;
581         ertm_info.user_tx_buf_size  = MCA_USER_TX_BUF_SIZE;
582         ertm_info.fcr_rx_buf_size   = MCA_FCR_RX_BUF_SIZE;
583         ertm_info.fcr_tx_buf_size   = MCA_FCR_TX_BUF_SIZE;
584     }
585     return L2CA_ErtmConnectReq (psm, bd_addr, &ertm_info);
586 }
587 
588