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 Control Channel Action 22 * Functions. 23 * 24 ******************************************************************************/ 25 #include <string.h> 26 #include "bt_common.h" 27 #include "bt_target.h" 28 #include "bt_utils.h" 29 #include "btm_api.h" 30 #include "mca_api.h" 31 #include "mca_defs.h" 32 #include "mca_int.h" 33 #include "osi/include/osi.h" 34 35 #include "btu.h" 36 37 /***************************************************************************** 38 * constants 39 ****************************************************************************/ 40 /******************************************************************************* 41 * 42 * Function mca_ccb_rsp_tout 43 * 44 * Description This function processes the response timeout. 45 * 46 * Returns void. 47 * 48 ******************************************************************************/ 49 void mca_ccb_rsp_tout(tMCA_CCB* p_ccb, UNUSED_ATTR tMCA_CCB_EVT* p_data) { 50 tMCA_CTRL evt_data; 51 52 mca_ccb_report_event(p_ccb, MCA_RSP_TOUT_IND_EVT, &evt_data); 53 } 54 55 /******************************************************************************* 56 * 57 * Function mca_ccb_report_event 58 * 59 * Description This function reports the given event. 60 * 61 * Returns void. 62 * 63 ******************************************************************************/ 64 void mca_ccb_report_event(tMCA_CCB* p_ccb, uint8_t event, tMCA_CTRL* p_data) { 65 if (p_ccb && p_ccb->p_rcb && p_ccb->p_rcb->p_cback) 66 (*p_ccb->p_rcb->p_cback)(mca_rcb_to_handle(p_ccb->p_rcb), 67 mca_ccb_to_hdl(p_ccb), event, p_data); 68 } 69 70 /******************************************************************************* 71 * 72 * Function mca_ccb_free_msg 73 * 74 * Description This function frees the received message. 75 * 76 * Returns void. 77 * 78 ******************************************************************************/ 79 void mca_ccb_free_msg(UNUSED_ATTR tMCA_CCB* p_ccb, tMCA_CCB_EVT* p_data) { 80 osi_free(p_data); 81 } 82 83 /******************************************************************************* 84 * 85 * Function mca_ccb_snd_req 86 * 87 * Description This function builds a request and sends it to the peer. 88 * 89 * Returns void. 90 * 91 ******************************************************************************/ 92 void mca_ccb_snd_req(tMCA_CCB* p_ccb, tMCA_CCB_EVT* p_data) { 93 tMCA_CCB_MSG* p_msg = (tMCA_CCB_MSG*)p_data; 94 uint8_t *p, *p_start; 95 bool is_abort = false; 96 tMCA_DCB* p_dcb; 97 98 MCA_TRACE_DEBUG("mca_ccb_snd_req cong=%d req=%d", p_ccb->cong, 99 p_msg->op_code); 100 /* check for abort request */ 101 if ((p_ccb->status == MCA_CCB_STAT_PENDING) && 102 (p_msg->op_code == MCA_OP_MDL_ABORT_REQ)) { 103 p_dcb = mca_dcb_by_hdl(p_ccb->p_tx_req->dcb_idx); 104 /* the Abort API does not have the associated mdl_id. 105 * Get the mdl_id in dcb to compose the request */ 106 p_msg->mdl_id = p_dcb->mdl_id; 107 mca_dcb_event(p_dcb, MCA_DCB_API_CLOSE_EVT, NULL); 108 osi_free_and_reset((void**)&p_ccb->p_tx_req); 109 p_ccb->status = MCA_CCB_STAT_NORM; 110 is_abort = true; 111 } 112 113 /* no pending outgoing messages or it's an abort request for a pending data 114 * channel */ 115 if ((!p_ccb->p_tx_req) || is_abort) { 116 p_ccb->p_tx_req = p_msg; 117 if (!p_ccb->cong) { 118 BT_HDR* p_pkt = (BT_HDR*)osi_malloc(MCA_CTRL_MTU + sizeof(BT_HDR)); 119 120 p_pkt->offset = L2CAP_MIN_OFFSET; 121 p = p_start = (uint8_t*)(p_pkt + 1) + L2CAP_MIN_OFFSET; 122 *p++ = p_msg->op_code; 123 UINT16_TO_BE_STREAM(p, p_msg->mdl_id); 124 if (p_msg->op_code == MCA_OP_MDL_CREATE_REQ) { 125 *p++ = p_msg->mdep_id; 126 *p++ = p_msg->param; 127 } 128 p_msg->hdr.layer_specific = true; /* mark this message as sent */ 129 p_pkt->len = p - p_start; 130 L2CA_DataWrite(p_ccb->lcid, p_pkt); 131 period_ms_t interval_ms = p_ccb->p_rcb->reg.rsp_tout * 1000; 132 alarm_set_on_mloop(p_ccb->mca_ccb_timer, interval_ms, 133 mca_ccb_timer_timeout, p_ccb); 134 } 135 /* else the L2CAP channel is congested. keep the message to be sent later */ 136 } else { 137 MCA_TRACE_WARNING("dropping api req"); 138 osi_free(p_data); 139 } 140 } 141 142 /******************************************************************************* 143 * 144 * Function mca_ccb_snd_rsp 145 * 146 * Description This function builds a response and sends it to 147 * the peer. 148 * 149 * Returns void. 150 * 151 ******************************************************************************/ 152 void mca_ccb_snd_rsp(tMCA_CCB* p_ccb, tMCA_CCB_EVT* p_data) { 153 tMCA_CCB_MSG* p_msg = (tMCA_CCB_MSG*)p_data; 154 uint8_t *p, *p_start; 155 BT_HDR* p_pkt = (BT_HDR*)osi_malloc(MCA_CTRL_MTU + sizeof(BT_HDR)); 156 157 MCA_TRACE_DEBUG("%s cong=%d req=%d", __func__, p_ccb->cong, p_msg->op_code); 158 /* assume that API functions verified the parameters */ 159 160 p_pkt->offset = L2CAP_MIN_OFFSET; 161 p = p_start = (uint8_t*)(p_pkt + 1) + L2CAP_MIN_OFFSET; 162 *p++ = p_msg->op_code; 163 *p++ = p_msg->rsp_code; 164 UINT16_TO_BE_STREAM(p, p_msg->mdl_id); 165 // Only add extra parameters for MCA_RSP_SUCCESS message 166 if (p_msg->rsp_code == MCA_RSP_SUCCESS) { 167 // Append MDL configuration parameters 168 if (p_msg->op_code == MCA_OP_MDL_CREATE_RSP) { 169 *p++ = p_msg->param; 170 } 171 // Check MDL 172 if (p_msg->op_code == MCA_OP_MDL_CREATE_RSP || 173 p_msg->op_code == MCA_OP_MDL_RECONNECT_RSP) { 174 mca_dcb_by_hdl(p_msg->dcb_idx); 175 BTM_SetSecurityLevel(false, "", BTM_SEC_SERVICE_MCAP_DATA, 176 p_ccb->sec_mask, p_ccb->p_rcb->reg.data_psm, 177 BTM_SEC_PROTO_MCA, p_msg->dcb_idx); 178 p_ccb->status = MCA_CCB_STAT_PENDING; 179 /* set p_tx_req to block API_REQ/API_RSP before DL is up */ 180 osi_free_and_reset((void**)&p_ccb->p_tx_req); 181 p_ccb->p_tx_req = p_ccb->p_rx_msg; 182 p_ccb->p_rx_msg = NULL; 183 p_ccb->p_tx_req->dcb_idx = p_msg->dcb_idx; 184 } 185 } 186 187 osi_free_and_reset((void**)&p_ccb->p_rx_msg); 188 p_pkt->len = p - p_start; 189 L2CA_DataWrite(p_ccb->lcid, p_pkt); 190 } 191 192 /******************************************************************************* 193 * 194 * Function mca_ccb_do_disconn 195 * 196 * Description This function closes a control channel. 197 * 198 * Returns void. 199 * 200 ******************************************************************************/ 201 void mca_ccb_do_disconn(tMCA_CCB* p_ccb, UNUSED_ATTR tMCA_CCB_EVT* p_data) { 202 mca_dcb_close_by_mdl_id(p_ccb, MCA_ALL_MDL_ID); 203 L2CA_DisconnectReq(p_ccb->lcid); 204 } 205 206 /******************************************************************************* 207 * 208 * Function mca_ccb_cong 209 * 210 * Description This function sets the congestion state for the CCB. 211 * 212 * Returns void. 213 * 214 ******************************************************************************/ 215 void mca_ccb_cong(tMCA_CCB* p_ccb, tMCA_CCB_EVT* p_data) { 216 MCA_TRACE_DEBUG("mca_ccb_cong cong=%d/%d", p_ccb->cong, p_data->llcong); 217 p_ccb->cong = p_data->llcong; 218 if (!p_ccb->cong) { 219 /* if there's a held packet, send it now */ 220 if (p_ccb->p_tx_req && !p_ccb->p_tx_req->hdr.layer_specific) { 221 p_data = (tMCA_CCB_EVT*)p_ccb->p_tx_req; 222 p_ccb->p_tx_req = NULL; 223 mca_ccb_snd_req(p_ccb, p_data); 224 } 225 } 226 } 227 228 /******************************************************************************* 229 * 230 * Function mca_ccb_hdl_req 231 * 232 * Description This function is called when a MCAP request is received from 233 * the peer. It calls the application callback function to 234 * report the event. 235 * 236 * Returns void. 237 * 238 ******************************************************************************/ 239 void mca_ccb_hdl_req(tMCA_CCB* p_ccb, tMCA_CCB_EVT* p_data) { 240 BT_HDR* p_pkt = &p_data->hdr; 241 uint8_t *p, *p_start; 242 tMCA_DCB* p_dcb; 243 tMCA_CTRL evt_data; 244 tMCA_CCB_MSG* p_rx_msg = NULL; 245 uint8_t reject_code = MCA_RSP_NO_RESOURCE; 246 bool send_rsp = false; 247 bool check_req = false; 248 uint8_t reject_opcode; 249 250 MCA_TRACE_DEBUG("mca_ccb_hdl_req status:%d", p_ccb->status); 251 p_rx_msg = (tMCA_CCB_MSG*)p_pkt; 252 p = (uint8_t*)(p_pkt + 1) + p_pkt->offset; 253 evt_data.hdr.op_code = *p++; 254 BE_STREAM_TO_UINT16(evt_data.hdr.mdl_id, p); 255 reject_opcode = evt_data.hdr.op_code + 1; 256 257 MCA_TRACE_DEBUG("received mdl id: %d ", evt_data.hdr.mdl_id); 258 if (p_ccb->status == MCA_CCB_STAT_PENDING) { 259 MCA_TRACE_DEBUG("received req inpending state"); 260 /* allow abort in pending state */ 261 if ((p_ccb->status == MCA_CCB_STAT_PENDING) && 262 (evt_data.hdr.op_code == MCA_OP_MDL_ABORT_REQ)) { 263 reject_code = MCA_RSP_SUCCESS; 264 send_rsp = true; 265 /* clear the pending status */ 266 p_ccb->status = MCA_CCB_STAT_NORM; 267 if (p_ccb->p_tx_req && 268 ((p_dcb = mca_dcb_by_hdl(p_ccb->p_tx_req->dcb_idx)) != NULL)) { 269 mca_dcb_dealloc(p_dcb, NULL); 270 osi_free_and_reset((void**)&p_ccb->p_tx_req); 271 } 272 } else 273 reject_code = MCA_RSP_BAD_OP; 274 } else if (p_ccb->p_rx_msg) { 275 MCA_TRACE_DEBUG("still handling prev req"); 276 /* still holding previous message, reject this new one ?? */ 277 278 } else if (p_ccb->p_tx_req) { 279 MCA_TRACE_DEBUG("still waiting for a response ctrl_vpsm:0x%x", 280 p_ccb->ctrl_vpsm); 281 /* sent a request; waiting for response */ 282 if (p_ccb->ctrl_vpsm == 0) { 283 MCA_TRACE_DEBUG("local is ACP. accept the cmd from INT"); 284 /* local is acceptor, need to handle the request */ 285 check_req = true; 286 reject_code = MCA_RSP_SUCCESS; 287 /* drop the previous request */ 288 if ((p_ccb->p_tx_req->op_code == MCA_OP_MDL_CREATE_REQ) && 289 ((p_dcb = mca_dcb_by_hdl(p_ccb->p_tx_req->dcb_idx)) != NULL)) { 290 mca_dcb_dealloc(p_dcb, NULL); 291 } 292 osi_free_and_reset((void**)&p_ccb->p_tx_req); 293 mca_stop_timer(p_ccb); 294 } else { 295 /* local is initiator, ignore the req */ 296 osi_free(p_pkt); 297 return; 298 } 299 } else if (p_pkt->layer_specific != MCA_RSP_SUCCESS) { 300 reject_code = (uint8_t)p_pkt->layer_specific; 301 if (((evt_data.hdr.op_code >= MCA_NUM_STANDARD_OPCODE) && 302 (evt_data.hdr.op_code < MCA_FIRST_SYNC_OP)) || 303 (evt_data.hdr.op_code > MCA_LAST_SYNC_OP)) { 304 /* invalid op code */ 305 reject_opcode = MCA_OP_ERROR_RSP; 306 evt_data.hdr.mdl_id = 0; 307 } 308 } else { 309 check_req = true; 310 reject_code = MCA_RSP_SUCCESS; 311 } 312 313 if (check_req) { 314 if (reject_code == MCA_RSP_SUCCESS) { 315 reject_code = MCA_RSP_BAD_MDL; 316 if (MCA_IS_VALID_MDL_ID(evt_data.hdr.mdl_id) || 317 ((evt_data.hdr.mdl_id == MCA_ALL_MDL_ID) && 318 (evt_data.hdr.op_code == MCA_OP_MDL_DELETE_REQ))) { 319 reject_code = MCA_RSP_SUCCESS; 320 /* mdl_id is valid according to the spec */ 321 switch (evt_data.hdr.op_code) { 322 case MCA_OP_MDL_CREATE_REQ: 323 evt_data.create_ind.dep_id = *p++; 324 evt_data.create_ind.cfg = *p++; 325 p_rx_msg->mdep_id = evt_data.create_ind.dep_id; 326 if (!mca_is_valid_dep_id(p_ccb->p_rcb, p_rx_msg->mdep_id)) { 327 MCA_TRACE_ERROR("%s: Invalid local MDEP ID %d", __func__, 328 p_rx_msg->mdep_id); 329 reject_code = MCA_RSP_BAD_MDEP; 330 } else if (mca_ccb_uses_mdl_id(p_ccb, evt_data.hdr.mdl_id)) { 331 MCA_TRACE_DEBUG("the mdl_id is currently used in the CL(create)"); 332 mca_dcb_close_by_mdl_id(p_ccb, evt_data.hdr.mdl_id); 333 } else { 334 /* check if this dep still have MDL available */ 335 if (mca_dep_free_mdl(p_ccb, evt_data.create_ind.dep_id) == 0) { 336 MCA_TRACE_ERROR("%s: MAX_MDL is used by MDEP %d", __func__, 337 evt_data.create_ind.dep_id); 338 reject_code = MCA_RSP_MDEP_BUSY; 339 } 340 } 341 break; 342 343 case MCA_OP_MDL_RECONNECT_REQ: 344 if (mca_ccb_uses_mdl_id(p_ccb, evt_data.hdr.mdl_id)) { 345 MCA_TRACE_ERROR("%s: MDL_ID %d busy, in CL(reconn)", __func__, 346 evt_data.hdr.mdl_id); 347 reject_code = MCA_RSP_MDL_BUSY; 348 } 349 break; 350 351 case MCA_OP_MDL_ABORT_REQ: 352 reject_code = MCA_RSP_BAD_OP; 353 break; 354 355 case MCA_OP_MDL_DELETE_REQ: 356 /* delete the associated mdl */ 357 mca_dcb_close_by_mdl_id(p_ccb, evt_data.hdr.mdl_id); 358 send_rsp = true; 359 break; 360 } 361 } 362 } 363 } 364 365 if (((reject_code != MCA_RSP_SUCCESS) && 366 (evt_data.hdr.op_code != MCA_OP_SYNC_INFO_IND)) || 367 send_rsp) { 368 BT_HDR* p_buf = (BT_HDR*)osi_malloc(MCA_CTRL_MTU + sizeof(BT_HDR)); 369 p_buf->offset = L2CAP_MIN_OFFSET; 370 p = p_start = (uint8_t*)(p_buf + 1) + L2CAP_MIN_OFFSET; 371 *p++ = reject_opcode; 372 *p++ = reject_code; 373 bool valid_response = true; 374 switch (reject_opcode) { 375 // Fill in the rest of standard opcode response packet with mdl_id 376 case MCA_OP_ERROR_RSP: 377 case MCA_OP_MDL_CREATE_RSP: 378 case MCA_OP_MDL_RECONNECT_RSP: 379 case MCA_OP_MDL_ABORT_RSP: 380 case MCA_OP_MDL_DELETE_RSP: 381 UINT16_TO_BE_STREAM(p, evt_data.hdr.mdl_id); 382 break; 383 // Fill in the rest of clock sync opcode response packet with 0 384 case MCA_OP_SYNC_CAP_RSP: 385 // Page 37/58 MCAP V1.0 Spec: Total length (9) - 2 = 7 386 memset(p, 0, 7); 387 p += 7; 388 break; 389 case MCA_OP_SYNC_SET_RSP: 390 // Page 39/58 MCAP V1.0 Spec: Total length (16) - 2 = 14 391 memset(p, 0, 14); 392 p += 14; 393 break; 394 default: 395 MCA_TRACE_ERROR("%s: reject_opcode 0x%02x not recognized", __func__, 396 reject_opcode); 397 valid_response = false; 398 break; 399 } 400 if (valid_response) { 401 p_buf->len = p - p_start; 402 MCA_TRACE_ERROR("%s: reject_opcode=0x%02x, reject_code=0x%02x, length=%d", 403 __func__, reject_opcode, reject_code, p_buf->len); 404 L2CA_DataWrite(p_ccb->lcid, p_buf); 405 } else { 406 osi_free(p_buf); 407 } 408 } 409 410 if (reject_code == MCA_RSP_SUCCESS) { 411 /* use the received GKI buffer to store information to double check response 412 * API */ 413 p_rx_msg->op_code = evt_data.hdr.op_code; 414 p_rx_msg->mdl_id = evt_data.hdr.mdl_id; 415 p_ccb->p_rx_msg = p_rx_msg; 416 if (send_rsp) { 417 osi_free(p_pkt); 418 p_ccb->p_rx_msg = NULL; 419 } 420 mca_ccb_report_event(p_ccb, evt_data.hdr.op_code, &evt_data); 421 } else 422 osi_free(p_pkt); 423 } 424 425 /******************************************************************************* 426 * 427 * Function mca_ccb_hdl_rsp 428 * 429 * Description This function is called when a MCAP response is received 430 * from the peer. It calls the application callback function 431 * with the results. 432 * 433 * Returns void. 434 * 435 ******************************************************************************/ 436 void mca_ccb_hdl_rsp(tMCA_CCB* p_ccb, tMCA_CCB_EVT* p_data) { 437 BT_HDR* p_pkt = &p_data->hdr; 438 uint8_t* p; 439 tMCA_CTRL evt_data; 440 bool chk_mdl = false; 441 tMCA_DCB* p_dcb; 442 tMCA_RESULT result = MCA_BAD_HANDLE; 443 tMCA_TC_TBL* p_tbl; 444 445 if (p_ccb->p_tx_req) { 446 /* verify that the received response matches the sent request */ 447 p = (uint8_t*)(p_pkt + 1) + p_pkt->offset; 448 evt_data.hdr.op_code = *p++; 449 if ((evt_data.hdr.op_code == 0) || 450 ((p_ccb->p_tx_req->op_code + 1) == evt_data.hdr.op_code)) { 451 evt_data.rsp.rsp_code = *p++; 452 mca_stop_timer(p_ccb); 453 BE_STREAM_TO_UINT16(evt_data.hdr.mdl_id, p); 454 if (evt_data.hdr.op_code == MCA_OP_MDL_CREATE_RSP) { 455 evt_data.create_cfm.cfg = *p++; 456 chk_mdl = true; 457 } else if (evt_data.hdr.op_code == MCA_OP_MDL_RECONNECT_RSP) 458 chk_mdl = true; 459 460 if (chk_mdl) { 461 p_dcb = mca_dcb_by_hdl(p_ccb->p_tx_req->dcb_idx); 462 if (evt_data.rsp.rsp_code == MCA_RSP_SUCCESS) { 463 if (evt_data.hdr.mdl_id != p_dcb->mdl_id) { 464 MCA_TRACE_ERROR("peer's mdl_id=%d != our mdl_id=%d", 465 evt_data.hdr.mdl_id, p_dcb->mdl_id); 466 /* change the response code to be an error */ 467 if (evt_data.rsp.rsp_code == MCA_RSP_SUCCESS) { 468 evt_data.rsp.rsp_code = MCA_RSP_BAD_MDL; 469 /* send Abort */ 470 p_ccb->status = MCA_CCB_STAT_PENDING; 471 MCA_Abort(mca_ccb_to_hdl(p_ccb)); 472 } 473 } else if (p_dcb->p_chnl_cfg) { 474 /* the data channel configuration is known. Proceed with data 475 * channel initiation */ 476 BTM_SetSecurityLevel(true, "", BTM_SEC_SERVICE_MCAP_DATA, 477 p_ccb->sec_mask, p_ccb->data_vpsm, 478 BTM_SEC_PROTO_MCA, p_ccb->p_tx_req->dcb_idx); 479 p_dcb->lcid = mca_l2c_open_req(p_ccb->peer_addr, p_ccb->data_vpsm, 480 p_dcb->p_chnl_cfg); 481 if (p_dcb->lcid) { 482 p_tbl = mca_tc_tbl_dalloc(p_dcb); 483 if (p_tbl) { 484 p_tbl->state = MCA_TC_ST_CONN; 485 p_ccb->status = MCA_CCB_STAT_PENDING; 486 result = MCA_SUCCESS; 487 } 488 } 489 } else { 490 /* mark this MCL as pending and wait for MCA_DataChnlCfg */ 491 p_ccb->status = MCA_CCB_STAT_PENDING; 492 result = MCA_SUCCESS; 493 } 494 } 495 496 if (result != MCA_SUCCESS && p_dcb) { 497 mca_dcb_dealloc(p_dcb, NULL); 498 } 499 } /* end of chk_mdl */ 500 501 if (p_ccb->status != MCA_CCB_STAT_PENDING) 502 osi_free_and_reset((void**)&p_ccb->p_tx_req); 503 mca_ccb_report_event(p_ccb, evt_data.hdr.op_code, &evt_data); 504 } 505 /* else a bad response is received */ 506 } else { 507 /* not expecting any response. drop it */ 508 MCA_TRACE_WARNING("dropping received rsp (not expecting a response)"); 509 } 510 osi_free(p_data); 511 } 512 513 /******************************************************************************* 514 * 515 * Function mca_ccb_ll_open 516 * 517 * Description This function is called to report MCA_CONNECT_IND_EVT event. 518 * It also clears the congestion flag (ccb.cong). 519 * 520 * Returns void. 521 * 522 ******************************************************************************/ 523 void mca_ccb_ll_open(tMCA_CCB* p_ccb, tMCA_CCB_EVT* p_data) { 524 tMCA_CTRL evt_data; 525 p_ccb->cong = false; 526 evt_data.connect_ind.mtu = p_data->open.peer_mtu; 527 evt_data.connect_ind.bd_addr = p_ccb->peer_addr; 528 mca_ccb_report_event(p_ccb, MCA_CONNECT_IND_EVT, &evt_data); 529 } 530 531 /******************************************************************************* 532 * 533 * Function mca_ccb_dl_open 534 * 535 * Description This function is called when data channel is open. It clears 536 * p_tx_req to allow other message exchage on this CL. 537 * 538 * Returns void. 539 * 540 ******************************************************************************/ 541 void mca_ccb_dl_open(tMCA_CCB* p_ccb, UNUSED_ATTR tMCA_CCB_EVT* p_data) { 542 osi_free_and_reset((void**)&p_ccb->p_tx_req); 543 osi_free_and_reset((void**)&p_ccb->p_rx_msg); 544 p_ccb->status = MCA_CCB_STAT_NORM; 545 } 546