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 *******************************************************************************/
mca_sec_check_complete_term(BD_ADDR bd_addr,void * p_ref_data,UINT8 res)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 *******************************************************************************/
mca_sec_check_complete_orig(BD_ADDR bd_addr,void * p_ref_data,UINT8 res)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 *******************************************************************************/
mca_l2c_cconn_ind_cback(BD_ADDR bd_addr,UINT16 lcid,UINT16 psm,UINT8 id)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 *******************************************************************************/
mca_l2c_dconn_ind_cback(BD_ADDR bd_addr,UINT16 lcid,UINT16 psm,UINT8 id)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 *******************************************************************************/
mca_l2c_connect_cfm_cback(UINT16 lcid,UINT16 result)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 *******************************************************************************/
mca_l2c_config_cfm_cback(UINT16 lcid,tL2CAP_CFG_INFO * p_cfg)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 *******************************************************************************/
mca_l2c_config_ind_cback(UINT16 lcid,tL2CAP_CFG_INFO * p_cfg)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 *******************************************************************************/
mca_l2c_disconnect_ind_cback(UINT16 lcid,BOOLEAN ack_needed)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 *******************************************************************************/
mca_l2c_disconnect_cfm_cback(UINT16 lcid,UINT16 result)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 *******************************************************************************/
mca_l2c_congestion_ind_cback(UINT16 lcid,BOOLEAN is_congested)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 *******************************************************************************/
mca_l2c_data_ind_cback(UINT16 lcid,BT_HDR * p_buf)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 *******************************************************************************/
mca_l2c_open_req(BD_ADDR bd_addr,UINT16 psm,const tMCA_CHNL_CFG * p_chnl_cfg)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