/****************************************************************************** * * Copyright 2006-2012 Broadcom Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ******************************************************************************/ /****************************************************************************** * * This module contains the action functions associated with the channel * control block state machine. * ******************************************************************************/ #include #include "avdt_api.h" #include "avdt_int.h" #include "avdtc_api.h" #include "bt_common.h" #include "bt_target.h" #include "bt_types.h" #include "bt_utils.h" #include "btm_api.h" #include "btu.h" #include "osi/include/osi.h" #include "stack/btm/btm_sec.h" /******************************************************************************* * * Function avdt_ccb_clear_ccb * * Description This function clears out certain buffers, queues, and * other data elements of a ccb. * * * Returns void. * ******************************************************************************/ static void avdt_ccb_clear_ccb(AvdtpCcb* p_ccb) { BT_HDR* p_buf; /* clear certain ccb variables */ p_ccb->cong = false; p_ccb->ret_count = 0; /* free message being fragmented */ osi_free_and_reset((void**)&p_ccb->p_curr_msg); /* free message being reassembled */ osi_free_and_reset((void**)&p_ccb->p_rx_msg); /* clear out response queue */ while ((p_buf = (BT_HDR*)fixed_queue_try_dequeue(p_ccb->rsp_q)) != NULL) osi_free(p_buf); } /******************************************************************************* * * Function avdt_ccb_chan_open * * Description This function calls avdt_ad_open_req() to * initiate a signaling channel connection. * * * Returns void. * ******************************************************************************/ void avdt_ccb_chan_open(AvdtpCcb* p_ccb, UNUSED_ATTR tAVDT_CCB_EVT* p_data) { avdt_ad_open_req(AVDT_CHAN_SIG, p_ccb, NULL, AVDT_INT); } /******************************************************************************* * * Function avdt_ccb_chan_close * * Description This function calls avdt_ad_close_req() to close a * signaling channel connection. * * * Returns void. * ******************************************************************************/ void avdt_ccb_chan_close(AvdtpCcb* p_ccb, UNUSED_ATTR tAVDT_CCB_EVT* p_data) { /* close the transport channel used by this CCB */ avdt_ad_close_req(AVDT_CHAN_SIG, p_ccb, NULL); } /******************************************************************************* * * Function avdt_ccb_chk_close * * Description This function checks for active streams on this CCB. * If there are none, it starts an idle timer. * * * Returns void. * ******************************************************************************/ void avdt_ccb_chk_close(AvdtpCcb* p_ccb, UNUSED_ATTR tAVDT_CCB_EVT* p_data) { int i; AvdtpScb* p_scb = &(p_ccb->scb[0]); /* see if there are any active scbs associated with this ccb */ for (i = 0; i < AVDT_NUM_SEPS; i++, p_scb++) { if ((p_scb->allocated) && (p_scb->p_ccb == p_ccb)) { break; } } /* if no active scbs start idle timer */ if (i == AVDT_NUM_SEPS) { alarm_cancel(p_ccb->ret_ccb_timer); alarm_cancel(p_ccb->rsp_ccb_timer); uint64_t interval_ms = avdtp_cb.rcb.idle_tout * 1000; alarm_set_on_mloop(p_ccb->idle_ccb_timer, interval_ms, avdt_ccb_idle_ccb_timer_timeout, p_ccb); } } /******************************************************************************* * * Function avdt_ccb_hdl_discover_cmd * * Description This function is called when a discover command is * received from the peer. It gathers up the stream * information for all allocated streams and initiates * sending of a discover response. * * * Returns void. * ******************************************************************************/ void avdt_ccb_hdl_discover_cmd(AvdtpCcb* p_ccb, tAVDT_CCB_EVT* p_data) { tAVDT_SEP_INFO sep_info[AVDT_NUM_SEPS]; AvdtpScb* p_scb = &(p_ccb->scb[0]); AVDT_TRACE_DEBUG("%s: p_ccb index=%d", __func__, avdt_ccb_to_idx(p_ccb)); p_data->msg.discover_rsp.p_sep_info = sep_info; p_data->msg.discover_rsp.num_seps = 0; /* for all allocated scbs */ for (int i = 0; i < AVDT_NUM_SEPS; i++, p_scb++) { if (p_scb->allocated) { /* copy sep info */ sep_info[p_data->msg.discover_rsp.num_seps].in_use = p_scb->in_use; sep_info[p_data->msg.discover_rsp.num_seps].seid = p_scb->ScbHandle(); sep_info[p_data->msg.discover_rsp.num_seps].media_type = p_scb->stream_config.media_type; sep_info[p_data->msg.discover_rsp.num_seps].tsep = p_scb->stream_config.tsep; p_data->msg.discover_rsp.num_seps++; } } /* send response */ avdt_ccb_event(p_ccb, AVDT_CCB_API_DISCOVER_RSP_EVT, p_data); } /******************************************************************************* * * Function avdt_ccb_hdl_discover_rsp * * Description This function is called when a discover response or * reject is received from the peer. It calls the application * callback function with the results. * * * Returns void. * ******************************************************************************/ void avdt_ccb_hdl_discover_rsp(AvdtpCcb* p_ccb, tAVDT_CCB_EVT* p_data) { /* we're done with procedure */ p_ccb->proc_busy = false; /* call app callback with results */ (*p_ccb->proc_cback)(0, p_ccb->peer_addr, AVDT_DISCOVER_CFM_EVT, (tAVDT_CTRL*)(&p_data->msg.discover_rsp), p_ccb->BtaAvScbIndex()); } /******************************************************************************* * * Function avdt_ccb_hdl_getcap_cmd * * Description This function is called when a get capabilities command * is received from the peer. It retrieves the stream * configuration for the requested stream and initiates * sending of a get capabilities response. * * * Returns void. * ******************************************************************************/ void avdt_ccb_hdl_getcap_cmd(AvdtpCcb* p_ccb, tAVDT_CCB_EVT* p_data) { /* look up scb for seid sent to us */ AvdtpScb* p_scb = avdt_scb_by_hdl(p_data->msg.single.seid); if (p_scb == nullptr) { /* not ok, send reject */ p_data->msg.hdr.err_code = AVDT_ERR_BAD_STATE; p_data->msg.hdr.err_param = p_data->msg.single.seid; avdt_msg_send_rej(p_ccb, AVDT_SIG_START, &p_data->msg); return; } p_data->msg.svccap.p_cfg = &p_scb->stream_config.cfg; avdt_ccb_event(p_ccb, AVDT_CCB_API_GETCAP_RSP_EVT, p_data); } /******************************************************************************* * * Function avdt_ccb_hdl_getcap_rsp * * Description This function is called with a get capabilities response * or reject is received from the peer. It calls the * application callback function with the results. * * * Returns void. * ******************************************************************************/ void avdt_ccb_hdl_getcap_rsp(AvdtpCcb* p_ccb, tAVDT_CCB_EVT* p_data) { /* we're done with procedure */ p_ccb->proc_busy = false; /* call app callback with results */ (*p_ccb->proc_cback)(0, p_ccb->peer_addr, AVDT_GETCAP_CFM_EVT, (tAVDT_CTRL*)(&p_data->msg.svccap), p_ccb->BtaAvScbIndex()); } /******************************************************************************* * * Function avdt_ccb_hdl_start_cmd * * Description This function is called when a start command is received * from the peer. It verifies that all requested streams * are in the proper state. If so, it initiates sending of * a start response. Otherwise it sends a start reject. * * * Returns void. * ******************************************************************************/ void avdt_ccb_hdl_start_cmd(AvdtpCcb* p_ccb, tAVDT_CCB_EVT* p_data) { uint8_t err_code = 0; /* verify all streams in the right state */ uint8_t seid = avdt_scb_verify(p_ccb, AVDT_VERIFY_START, p_data->msg.multi.seid_list, p_data->msg.multi.num_seps, &err_code); if (seid == 0 && err_code == 0) { /* we're ok, send response */ avdt_ccb_event(p_ccb, AVDT_CCB_API_START_RSP_EVT, p_data); } else { /* not ok, send reject */ p_data->msg.hdr.err_code = err_code; p_data->msg.hdr.err_param = seid; avdt_msg_send_rej(p_ccb, AVDT_SIG_START, &p_data->msg); } } /******************************************************************************* * * Function avdt_ccb_hdl_start_rsp * * Description This function is called when a start response or reject * is received from the peer. Using the SEIDs stored in the * current command message, it sends a start response or start * reject event to each SCB associated with the command. * * * Returns void. * ******************************************************************************/ void avdt_ccb_hdl_start_rsp(AvdtpCcb* p_ccb, tAVDT_CCB_EVT* p_data) { uint8_t event; int i; uint8_t* p; AvdtpScb* p_scb; /* determine rsp or rej event */ event = (p_data->msg.hdr.err_code == 0) ? AVDT_SCB_MSG_START_RSP_EVT : AVDT_SCB_MSG_START_REJ_EVT; /* get to where seid's are stashed in current cmd */ p = (uint8_t*)(p_ccb->p_curr_cmd + 1); /* little trick here; length of current command equals number of streams */ for (i = 0; i < p_ccb->p_curr_cmd->len; i++) { p_scb = avdt_scb_by_hdl(p[i]); if (p_scb != NULL) { avdt_scb_event(p_scb, event, (tAVDT_SCB_EVT*)&p_data->msg); } } } /******************************************************************************* * * Function avdt_ccb_hdl_suspend_cmd * * Description This function is called when a suspend command is received * from the peer. It verifies that all requested streams are * in the proper state. If so, it initiates sending of a * suspend response. Otherwise it sends a suspend reject. * * * Returns void. * ******************************************************************************/ void avdt_ccb_hdl_suspend_cmd(AvdtpCcb* p_ccb, tAVDT_CCB_EVT* p_data) { uint8_t seid; uint8_t err_code = 0; /* verify all streams in the right state */ if ((seid = avdt_scb_verify(p_ccb, AVDT_VERIFY_SUSPEND, p_data->msg.multi.seid_list, p_data->msg.multi.num_seps, &err_code)) == 0 && err_code == 0) { /* we're ok, send response */ avdt_ccb_event(p_ccb, AVDT_CCB_API_SUSPEND_RSP_EVT, p_data); } else { /* not ok, send reject */ p_data->msg.hdr.err_code = err_code; p_data->msg.hdr.err_param = seid; avdt_msg_send_rej(p_ccb, AVDT_SIG_SUSPEND, &p_data->msg); } } /******************************************************************************* * * Function avdt_ccb_hdl_suspend_rsp * * Description This function is called when a suspend response or reject * is received from the peer. Using the SEIDs stored in the * current command message, it sends a suspend response or * suspend reject event to each SCB associated with the * command. * * * * Returns void. * ******************************************************************************/ void avdt_ccb_hdl_suspend_rsp(AvdtpCcb* p_ccb, tAVDT_CCB_EVT* p_data) { uint8_t event; int i; uint8_t* p; AvdtpScb* p_scb; /* determine rsp or rej event */ event = (p_data->msg.hdr.err_code == 0) ? AVDT_SCB_MSG_SUSPEND_RSP_EVT : AVDT_SCB_MSG_SUSPEND_REJ_EVT; /* get to where seid's are stashed in current cmd */ p = (uint8_t*)(p_ccb->p_curr_cmd + 1); /* little trick here; length of current command equals number of streams */ for (i = 0; i < p_ccb->p_curr_cmd->len; i++) { p_scb = avdt_scb_by_hdl(p[i]); if (p_scb != NULL) { avdt_scb_event(p_scb, event, (tAVDT_SCB_EVT*)&p_data->msg); } } } /******************************************************************************* * * Function avdt_ccb_snd_discover_cmd * * Description This function is called to send a discover command to the * peer. It copies variables needed for the procedure from * the event to the CCB. It marks the CCB as busy and then * sends a discover command. * * * Returns void. * ******************************************************************************/ void avdt_ccb_snd_discover_cmd(AvdtpCcb* p_ccb, tAVDT_CCB_EVT* p_data) { /* store info in ccb struct */ p_ccb->p_proc_data = p_data->discover.p_sep_info; p_ccb->proc_cback = p_data->discover.p_cback; p_ccb->proc_param = p_data->discover.num_seps; /* we're busy */ p_ccb->proc_busy = true; /* build and queue discover req */ avdt_msg_send_cmd(p_ccb, NULL, AVDT_SIG_DISCOVER, NULL); } /******************************************************************************* * * Function avdt_ccb_snd_discover_rsp * * Description This function is called to send a discover response to * the peer. It takes the stream information passed in the * event and sends a discover response. * * * Returns void. * ******************************************************************************/ void avdt_ccb_snd_discover_rsp(AvdtpCcb* p_ccb, tAVDT_CCB_EVT* p_data) { /* send response */ avdt_msg_send_rsp(p_ccb, AVDT_SIG_DISCOVER, &p_data->msg); } /******************************************************************************* * * Function avdt_ccb_snd_getcap_cmd * * Description This function is called to send a get capabilities command * to the peer. It copies variables needed for the procedure * from the event to the CCB. It marks the CCB as busy and * then sends a get capabilities command. * * * Returns void. * ******************************************************************************/ void avdt_ccb_snd_getcap_cmd(AvdtpCcb* p_ccb, tAVDT_CCB_EVT* p_data) { uint8_t sig_id = AVDT_SIG_GETCAP; /* store info in ccb struct */ p_ccb->p_proc_data = p_data->getcap.p_cfg; p_ccb->proc_cback = p_data->getcap.p_cback; /* we're busy */ p_ccb->proc_busy = true; /* build and queue discover req */ if (p_data->msg.hdr.sig_id == AVDT_SIG_GET_ALLCAP) sig_id = AVDT_SIG_GET_ALLCAP; avdt_msg_send_cmd(p_ccb, NULL, sig_id, (tAVDT_MSG*)&p_data->getcap.single); } /******************************************************************************* * * Function avdt_ccb_snd_getcap_rsp * * Description This function is called to send a get capabilities response * to the peer. It takes the stream information passed in the * event and sends a get capabilities response. * * * Returns void. * ******************************************************************************/ void avdt_ccb_snd_getcap_rsp(AvdtpCcb* p_ccb, tAVDT_CCB_EVT* p_data) { uint8_t sig_id = AVDT_SIG_GETCAP; if (p_data->msg.hdr.sig_id == AVDT_SIG_GET_ALLCAP) sig_id = AVDT_SIG_GET_ALLCAP; /* send response */ avdt_msg_send_rsp(p_ccb, sig_id, &p_data->msg); } /******************************************************************************* * * Function avdt_ccb_snd_start_cmd * * Description This function is called to send a start command to the * peer. It verifies that all requested streams are in the * proper state. If so, it sends a start command. Otherwise * send ourselves back a start reject. * * * Returns void. * ******************************************************************************/ void avdt_ccb_snd_start_cmd(AvdtpCcb* p_ccb, tAVDT_CCB_EVT* p_data) { int i; AvdtpScb* p_scb; tAVDT_MSG avdt_msg; uint8_t seid_list[AVDT_NUM_SEPS]; AVDT_TRACE_DEBUG("%s", __func__); /* make copy of our seid list */ memcpy(seid_list, p_data->msg.multi.seid_list, p_data->msg.multi.num_seps); /* verify all streams in the right state */ avdt_msg.hdr.err_param = avdt_scb_verify(p_ccb, AVDT_VERIFY_OPEN, p_data->msg.multi.seid_list, p_data->msg.multi.num_seps, &avdt_msg.hdr.err_code); if (avdt_msg.hdr.err_param == 0) { AVDT_TRACE_DEBUG("%s: AVDT_SIG_START", __func__); /* set peer seid list in messsage */ avdt_scb_peer_seid_list(&p_data->msg.multi); /* send command */ avdt_msg_send_cmd(p_ccb, seid_list, AVDT_SIG_START, &p_data->msg); } else { /* failed; send ourselves a reject for each stream */ for (i = 0; i < p_data->msg.multi.num_seps; i++) { p_scb = avdt_scb_by_hdl(seid_list[i]); if (p_scb != NULL) { AVDT_TRACE_DEBUG("%s: AVDT_SCB_MSG_START_REJ_EVT: i=%d", __func__, i); tAVDT_SCB_EVT avdt_scb_evt; avdt_scb_evt.msg.hdr = avdt_msg.hdr; avdt_scb_event(p_scb, AVDT_SCB_MSG_START_REJ_EVT, &avdt_scb_evt); } } } } /******************************************************************************* * * Function avdt_ccb_snd_start_rsp * * Description This function is called to send a start response to the * peer. It takes the stream information passed in the event * and sends a start response. Then it sends a start event * to the SCB for each stream. * * * Returns void. * ******************************************************************************/ void avdt_ccb_snd_start_rsp(AvdtpCcb* p_ccb, tAVDT_CCB_EVT* p_data) { AvdtpScb* p_scb; int i; /* send response message */ avdt_msg_send_rsp(p_ccb, AVDT_SIG_START, &p_data->msg); /* send start event to each scb */ for (i = 0; i < p_data->msg.multi.num_seps; i++) { p_scb = avdt_scb_by_hdl(p_data->msg.multi.seid_list[i]); if (p_scb != NULL) { avdt_scb_event(p_scb, AVDT_SCB_MSG_START_CMD_EVT, NULL); } } } /******************************************************************************* * * Function avdt_ccb_snd_suspend_cmd * * Description This function is called to send a suspend command to the * peer. It verifies that all requested streams are in the * proper state. If so, it sends a suspend command. * Otherwise it calls the callback function for each requested * stream and sends a suspend confirmation with failure. * * * Returns void. * ******************************************************************************/ void avdt_ccb_snd_suspend_cmd(AvdtpCcb* p_ccb, tAVDT_CCB_EVT* p_data) { int i; AvdtpScb* p_scb; tAVDT_MSG avdt_msg; uint8_t seid_list[AVDT_NUM_SEPS]; /* make copy of our seid list */ memcpy(seid_list, p_data->msg.multi.seid_list, p_data->msg.multi.num_seps); /* verify all streams in the right state */ avdt_msg.hdr.err_param = avdt_scb_verify(p_ccb, AVDT_VERIFY_STREAMING, p_data->msg.multi.seid_list, p_data->msg.multi.num_seps, &avdt_msg.hdr.err_code); if (avdt_msg.hdr.err_param == 0) { /* set peer seid list in messsage */ avdt_scb_peer_seid_list(&p_data->msg.multi); /* send command */ avdt_msg_send_cmd(p_ccb, seid_list, AVDT_SIG_SUSPEND, &p_data->msg); } else { /* failed; send ourselves a reject for each stream */ for (i = 0; i < p_data->msg.multi.num_seps; i++) { p_scb = avdt_scb_by_hdl(seid_list[i]); if (p_scb != NULL) { tAVDT_SCB_EVT avdt_scb_evt; avdt_scb_evt.msg.hdr = avdt_msg.hdr; avdt_scb_event(p_scb, AVDT_SCB_MSG_SUSPEND_REJ_EVT, &avdt_scb_evt); } } } } /******************************************************************************* * * Function avdt_ccb_snd_suspend_rsp * * Description This function is called to send a suspend response to the * peer. It takes the stream information passed in the event * and sends a suspend response. Then it sends a suspend event * to the SCB for each stream. * * * Returns void. * ******************************************************************************/ void avdt_ccb_snd_suspend_rsp(AvdtpCcb* p_ccb, tAVDT_CCB_EVT* p_data) { AvdtpScb* p_scb; int i; /* send response message */ avdt_msg_send_rsp(p_ccb, AVDT_SIG_SUSPEND, &p_data->msg); /* send start event to each scb */ for (i = 0; i < p_data->msg.multi.num_seps; i++) { p_scb = avdt_scb_by_hdl(p_data->msg.multi.seid_list[i]); if (p_scb != NULL) { avdt_scb_event(p_scb, AVDT_SCB_MSG_SUSPEND_CMD_EVT, NULL); } } } /******************************************************************************* * * Function avdt_ccb_clear_cmds * * Description This function is called when the signaling channel is * closed to clean up any pending commands. For each pending * command in the command queue, it frees the command and * calls the application callback function indicating failure. * Certain CCB variables are also initialized. * * * Returns void. * ******************************************************************************/ void avdt_ccb_clear_cmds(AvdtpCcb* p_ccb, UNUSED_ATTR tAVDT_CCB_EVT* p_data) { int i; AvdtpScb* p_scb = &(p_ccb->scb[0]); uint8_t err_code = AVDT_ERR_CONNECT; /* clear the ccb */ avdt_ccb_clear_ccb(p_ccb); /* clear out command queue; this is a little tricky here; we need ** to handle the case where there is a command on deck in p_curr_cmd, ** plus we need to clear out the queue */ do { /* we know p_curr_cmd = NULL after this */ tAVDT_CCB_EVT avdt_ccb_evt; avdt_ccb_evt.err_code = err_code; avdt_ccb_cmd_fail(p_ccb, &avdt_ccb_evt); /* set up next message */ p_ccb->p_curr_cmd = (BT_HDR*)fixed_queue_try_dequeue(p_ccb->cmd_q); } while (p_ccb->p_curr_cmd != NULL); /* send a CC_CLOSE_EVT any active scbs associated with this ccb */ for (i = 0; i < AVDT_NUM_SEPS; i++, p_scb++) { if ((p_scb->allocated) && (p_scb->p_ccb == p_ccb)) { avdt_scb_event(p_scb, AVDT_SCB_CC_CLOSE_EVT, NULL); } } } /******************************************************************************* * * Function avdt_ccb_cmd_fail * * Description This function is called when there is a response timeout. * The currently pending command is freed and we fake a * reject message back to ourselves. * * * Returns void. * ******************************************************************************/ void avdt_ccb_cmd_fail(AvdtpCcb* p_ccb, tAVDT_CCB_EVT* p_data) { tAVDT_MSG msg; uint8_t evt; AvdtpScb* p_scb; if (p_ccb->p_curr_cmd != NULL) { /* set up data */ msg.hdr.err_code = p_data->err_code; msg.hdr.err_param = 0; msg.hdr.ccb_idx = avdt_ccb_to_idx(p_ccb); /* pretend that we received a rej message */ evt = avdt_msg_rej_2_evt[p_ccb->p_curr_cmd->event - 1]; if (evt & AVDT_CCB_MKR) { tAVDT_CCB_EVT avdt_ccb_evt; avdt_ccb_evt.msg = msg; avdt_ccb_event(p_ccb, (uint8_t)(evt & ~AVDT_CCB_MKR), &avdt_ccb_evt); } else { /* we get the scb out of the current cmd */ p_scb = avdt_scb_by_hdl(*((uint8_t*)(p_ccb->p_curr_cmd + 1))); if (p_scb != NULL) { tAVDT_SCB_EVT avdt_scb_evt; avdt_scb_evt.msg = msg; avdt_scb_event(p_scb, evt, &avdt_scb_evt); } } osi_free_and_reset((void**)&p_ccb->p_curr_cmd); } } /******************************************************************************* * * Function avdt_ccb_free_cmd * * Description This function is called when a response is received for a * currently pending command. The command is freed. * * * Returns void. * ******************************************************************************/ void avdt_ccb_free_cmd(AvdtpCcb* p_ccb, UNUSED_ATTR tAVDT_CCB_EVT* p_data) { osi_free_and_reset((void**)&p_ccb->p_curr_cmd); } /******************************************************************************* * * Function avdt_ccb_cong_state * * Description This function is called to set the congestion state for * the CCB. * * * Returns void. * ******************************************************************************/ void avdt_ccb_cong_state(AvdtpCcb* p_ccb, tAVDT_CCB_EVT* p_data) { p_ccb->cong = p_data->llcong; } /******************************************************************************* * * Function avdt_ccb_ret_cmd * * Description This function is called to retransmit the currently * pending command. The retransmission count is incremented. * If the count reaches the maximum number of retransmissions, * the event is treated as a response timeout. * * * Returns void. * ******************************************************************************/ void avdt_ccb_ret_cmd(AvdtpCcb* p_ccb, tAVDT_CCB_EVT* p_data) { p_ccb->ret_count++; if (p_ccb->ret_count == AVDT_RET_MAX) { /* command failed */ p_ccb->ret_count = 0; tAVDT_CCB_EVT avdt_ccb_evt; avdt_ccb_evt.err_code = AVDT_ERR_TIMEOUT; avdt_ccb_cmd_fail(p_ccb, &avdt_ccb_evt); /* go to next queued command */ avdt_ccb_snd_cmd(p_ccb, p_data); } else { /* if command pending and we're not congested and not sending a fragment */ if ((!p_ccb->cong) && (p_ccb->p_curr_msg == NULL) && (p_ccb->p_curr_cmd != NULL)) { /* make copy of message in p_curr_cmd and send it */ BT_HDR* p_msg = (BT_HDR*)osi_malloc(AVDT_CMD_BUF_SIZE); memcpy(p_msg, p_ccb->p_curr_cmd, (sizeof(BT_HDR) + p_ccb->p_curr_cmd->offset + p_ccb->p_curr_cmd->len)); avdt_msg_send(p_ccb, p_msg); } /* restart ret timer */ alarm_cancel(p_ccb->idle_ccb_timer); alarm_cancel(p_ccb->rsp_ccb_timer); uint64_t interval_ms = avdtp_cb.rcb.ret_tout * 1000; alarm_set_on_mloop(p_ccb->ret_ccb_timer, interval_ms, avdt_ccb_ret_ccb_timer_timeout, p_ccb); } } /******************************************************************************* * * Function avdt_ccb_snd_cmd * * Description This function is called the send the next command, * if any, in the command queue. * * * Returns void. * ******************************************************************************/ void avdt_ccb_snd_cmd(AvdtpCcb* p_ccb, UNUSED_ATTR tAVDT_CCB_EVT* p_data) { BT_HDR* p_msg; /* do we have commands to send? send next command; make sure we're clear; ** not congested, not sending fragment, not waiting for response */ if ((!p_ccb->cong) && (p_ccb->p_curr_msg == NULL) && (p_ccb->p_curr_cmd == NULL)) { p_msg = (BT_HDR*)fixed_queue_try_dequeue(p_ccb->cmd_q); if (p_msg != NULL) { /* make a copy of buffer in p_curr_cmd */ p_ccb->p_curr_cmd = (BT_HDR*)osi_malloc(AVDT_CMD_BUF_SIZE); memcpy(p_ccb->p_curr_cmd, p_msg, (sizeof(BT_HDR) + p_msg->offset + p_msg->len)); avdt_msg_send(p_ccb, p_msg); } } } /******************************************************************************* * * Function avdt_ccb_snd_msg * * Description * * * Returns void. * ******************************************************************************/ void avdt_ccb_snd_msg(AvdtpCcb* p_ccb, UNUSED_ATTR tAVDT_CCB_EVT* p_data) { BT_HDR* p_msg; /* if not congested */ if (!p_ccb->cong) { /* are we sending a fragmented message? continue sending fragment */ if (p_ccb->p_curr_msg != NULL) { avdt_msg_send(p_ccb, NULL); } /* do we have responses to send? send them */ else if (!fixed_queue_is_empty(p_ccb->rsp_q)) { while ((p_msg = (BT_HDR*)fixed_queue_try_dequeue(p_ccb->rsp_q)) != NULL) { if (avdt_msg_send(p_ccb, p_msg)) { /* break out if congested */ break; } } } /* do we have commands to send? send next command */ avdt_ccb_snd_cmd(p_ccb, NULL); } } /******************************************************************************* * * Function avdt_ccb_set_reconn * * Description This function is called to enable a reconnect attempt when * a channel transitions from closing to idle state. It sets * the reconn variable to true. * * * Returns void. * ******************************************************************************/ void avdt_ccb_set_reconn(AvdtpCcb* p_ccb, UNUSED_ATTR tAVDT_CCB_EVT* p_data) { p_ccb->reconn = true; } /******************************************************************************* * * Function avdt_ccb_clr_reconn * * Description This function is called to clear the reconn variable. * * * Returns void. * ******************************************************************************/ void avdt_ccb_clr_reconn(AvdtpCcb* p_ccb, UNUSED_ATTR tAVDT_CCB_EVT* p_data) { p_ccb->reconn = false; } /******************************************************************************* * * Function avdt_ccb_chk_reconn * * Description This function is called to check if a reconnect attempt * is enabled. If enabled, it sends an AVDT_CCB_UL_OPEN_EVT * to the CCB. If disabled, the CCB is deallocated. * * * Returns void. * ******************************************************************************/ void avdt_ccb_chk_reconn(AvdtpCcb* p_ccb, UNUSED_ATTR tAVDT_CCB_EVT* p_data) { if (p_ccb->reconn) { p_ccb->reconn = false; /* clear out ccb */ avdt_ccb_clear_ccb(p_ccb); /* clear out current command, if any */ uint8_t err_code = AVDT_ERR_CONNECT; tAVDT_CCB_EVT avdt_ccb_evt; avdt_ccb_evt.err_code = err_code; avdt_ccb_cmd_fail(p_ccb, &avdt_ccb_evt); /* reopen the signaling channel */ avdt_ccb_event(p_ccb, AVDT_CCB_UL_OPEN_EVT, NULL); } else { avdt_ccb_ll_closed(p_ccb, NULL); } } /******************************************************************************* * * Function avdt_ccb_chk_timer * * Description This function stops the CCB timer if the idle timer is * running. * * * Returns void. * ******************************************************************************/ void avdt_ccb_chk_timer(AvdtpCcb* p_ccb, UNUSED_ATTR tAVDT_CCB_EVT* p_data) { alarm_cancel(p_ccb->idle_ccb_timer); } /******************************************************************************* * * Function avdt_ccb_set_conn * * Description Set CCB variables associated with AVDT_ConnectReq(). * * * Returns void. * ******************************************************************************/ void avdt_ccb_set_conn(AvdtpCcb* p_ccb, tAVDT_CCB_EVT* p_data) { /* save callback */ p_ccb->p_conn_cback = p_data->connect.p_cback; } /******************************************************************************* * * Function avdt_ccb_set_disconn * * Description Set CCB variables associated with AVDT_DisconnectReq(). * * * Returns void. * ******************************************************************************/ void avdt_ccb_set_disconn(AvdtpCcb* p_ccb, tAVDT_CCB_EVT* p_data) { /* AVDT_TRACE_EVENT("avdt_ccb_set_disconn:conn:x%x, api:x%x", p_ccb->p_conn_cback, p_data->disconnect.p_cback); */ /* save callback */ if (p_data->disconnect.p_cback) p_ccb->p_conn_cback = p_data->disconnect.p_cback; } /******************************************************************************* * * Function avdt_ccb_do_disconn * * Description Do action associated with AVDT_DisconnectReq(). * * * Returns void. * ******************************************************************************/ void avdt_ccb_do_disconn(AvdtpCcb* p_ccb, UNUSED_ATTR tAVDT_CCB_EVT* p_data) { /* clear any pending commands */ avdt_ccb_clear_cmds(p_ccb, NULL); /* close channel */ avdt_ccb_chan_close(p_ccb, NULL); } /******************************************************************************* * * Function avdt_ccb_ll_closed * * Description Clear commands from and deallocate CCB. * * * Returns void. * ******************************************************************************/ void avdt_ccb_ll_closed(AvdtpCcb* p_ccb, UNUSED_ATTR tAVDT_CCB_EVT* p_data) { tAVDT_CTRL_CBACK* p_cback; tAVDT_CTRL avdt_ctrl; AVDT_TRACE_DEBUG("%s peer %s", __func__, p_ccb->peer_addr.ToString().c_str()); /* clear any pending commands */ avdt_ccb_clear_cmds(p_ccb, NULL); /* save callback pointer, bd addr */ p_cback = p_ccb->p_conn_cback; if (!p_cback) p_cback = avdtp_cb.p_conn_cback; RawAddress bd_addr = p_ccb->peer_addr; uint8_t bta_av_scb_index = p_ccb->BtaAvScbIndex(); /* dealloc ccb */ avdt_ccb_dealloc(p_ccb, NULL); /* call callback */ if (p_cback) { avdt_ctrl.hdr.err_code = 0; (*p_cback)(0, bd_addr, AVDT_DISCONNECT_IND_EVT, &avdt_ctrl, bta_av_scb_index); } } /******************************************************************************* * * Function avdt_ccb_ll_opened * * Description Call callback on open. * * * Returns void. * ******************************************************************************/ void avdt_ccb_ll_opened(AvdtpCcb* p_ccb, tAVDT_CCB_EVT* p_data) { tAVDT_CTRL avdt_ctrl; AVDT_TRACE_DEBUG("%s peer %s BtaAvScbIndex=%d p_ccb=%p", __func__, p_ccb->peer_addr.ToString().c_str(), p_ccb->BtaAvScbIndex(), p_ccb); p_ccb->ll_opened = true; if (!p_ccb->p_conn_cback) p_ccb->p_conn_cback = avdtp_cb.p_conn_cback; /* call callback */ if (p_ccb->p_conn_cback) { avdt_ctrl.hdr.err_code = 0; avdt_ctrl.hdr.err_param = p_data->msg.hdr.err_param; (*p_ccb->p_conn_cback)(0, p_ccb->peer_addr, AVDT_CONNECT_IND_EVT, &avdt_ctrl, p_ccb->BtaAvScbIndex()); } }