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