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