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