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