| /****************************************************************************** |
| * |
| * Copyright 2002-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 stream |
| * control block state machine. |
| * |
| ******************************************************************************/ |
| |
| #define LOG_TAG "bluetooth" |
| |
| #include <bluetooth/log.h> |
| #include <string.h> |
| |
| #include "a2dp_codec_api.h" |
| #include "avdt_api.h" |
| #include "avdt_int.h" |
| #include "internal_include/bt_target.h" |
| #include "os/log.h" |
| #include "osi/include/allocator.h" |
| #include "osi/include/osi.h" |
| #include "stack/include/bt_hdr.h" |
| #include "stack/include/bt_types.h" |
| #include "types/raw_address.h" |
| |
| using namespace bluetooth; |
| |
| /* This table is used to lookup the callback event that matches a particular |
| * state machine API request event. Note that state machine API request |
| * events are at the beginning of the event list starting at zero, thus |
| * allowing for this table. |
| */ |
| const uint8_t avdt_scb_cback_evt[] = { |
| 0, /* API_REMOVE_EVT (no event) */ |
| AVDT_WRITE_CFM_EVT, /* API_WRITE_REQ_EVT */ |
| 0, /* API_GETCONFIG_REQ_EVT (no event) */ |
| 0, /* API_DELAY_RPT_REQ_EVT (no event) */ |
| AVDT_OPEN_CFM_EVT, /* API_SETCONFIG_REQ_EVT */ |
| AVDT_OPEN_CFM_EVT, /* API_OPEN_REQ_EVT */ |
| AVDT_CLOSE_CFM_EVT, /* API_CLOSE_REQ_EVT */ |
| AVDT_RECONFIG_CFM_EVT, /* API_RECONFIG_REQ_EVT */ |
| AVDT_SECURITY_CFM_EVT, /* API_SECURITY_REQ_EVT */ |
| 0 /* API_ABORT_REQ_EVT (no event) */ |
| }; |
| |
| /******************************************************************************* |
| * |
| * Function avdt_scb_gen_ssrc |
| * |
| * Description This function generates a SSRC number unique to the stream. |
| * |
| * Returns SSRC value. |
| * |
| ******************************************************************************/ |
| uint32_t avdt_scb_gen_ssrc(AvdtpScb* p_scb) { |
| /* combine the value of the media type and codec type of the SCB */ |
| return ((uint32_t)(p_scb->stream_config.cfg.codec_info[1] | |
| p_scb->stream_config.cfg.codec_info[2])); |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avdt_scb_hdl_abort_cmd |
| * |
| * Description This function sends the SCB an AVDT_SCB_API_ABORT_RSP_EVT |
| * to initiate sending of an abort response message. |
| * |
| * Returns Nothing. |
| * |
| ******************************************************************************/ |
| void avdt_scb_hdl_abort_cmd(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data) { |
| p_scb->role = AVDT_CLOSE_ACP; |
| avdt_scb_event(p_scb, AVDT_SCB_API_ABORT_RSP_EVT, p_data); |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avdt_scb_hdl_abort_rsp |
| * |
| * Description This function is an empty function; it serves as a |
| * placeholder for a conformance API action function. |
| * |
| * Returns Nothing. |
| * |
| ******************************************************************************/ |
| void avdt_scb_hdl_abort_rsp(UNUSED_ATTR AvdtpScb* p_scb, |
| UNUSED_ATTR tAVDT_SCB_EVT* p_data) { |
| return; |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avdt_scb_hdl_close_cmd |
| * |
| * Description This function sends the SCB an AVDT_SCB_API_CLOSE_RSP_EVT |
| * to initiate sending of a close response message. |
| * |
| * Returns Nothing. |
| * |
| ******************************************************************************/ |
| void avdt_scb_hdl_close_cmd(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data) { |
| p_scb->role = AVDT_CLOSE_ACP; |
| avdt_scb_event(p_scb, AVDT_SCB_API_CLOSE_RSP_EVT, p_data); |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avdt_scb_hdl_close_rsp |
| * |
| * Description This function sets the close_code variable to the error |
| * code returned in the close response. |
| * |
| * Returns Nothing. |
| * |
| ******************************************************************************/ |
| void avdt_scb_hdl_close_rsp(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data) { |
| p_scb->close_code = p_data->msg.hdr.err_code; |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avdt_scb_hdl_getconfig_cmd |
| * |
| * Description This function retrieves the configuration parameters of |
| * the SCB and sends the SCB an AVDT_SCB_API_GETCONFIG_RSP_EVT |
| * to initiate sending of a get configuration response message. |
| * |
| * Returns Nothing. |
| * |
| ******************************************************************************/ |
| void avdt_scb_hdl_getconfig_cmd(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data) { |
| p_data->msg.svccap.p_cfg = &p_scb->curr_cfg; |
| |
| avdt_scb_event(p_scb, AVDT_SCB_API_GETCONFIG_RSP_EVT, p_data); |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avdt_scb_hdl_getconfig_rsp |
| * |
| * Description This function is an empty function; it serves as a |
| * placeholder for a conformance API action function. |
| * |
| * Returns Nothing. |
| * |
| ******************************************************************************/ |
| void avdt_scb_hdl_getconfig_rsp(UNUSED_ATTR AvdtpScb* p_scb, |
| UNUSED_ATTR tAVDT_SCB_EVT* p_data) { |
| return; |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avdt_scb_hdl_open_cmd |
| * |
| * Description This function sends the SCB an AVDT_SCB_API_OPEN_RSP_EVT |
| * to initiate sending of an open response message. |
| * |
| * Returns Nothing. |
| * |
| ******************************************************************************/ |
| void avdt_scb_hdl_open_cmd(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data) { |
| avdt_scb_event(p_scb, AVDT_SCB_API_OPEN_RSP_EVT, p_data); |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avdt_scb_hdl_open_rej |
| * |
| * Description This function calls the application callback function |
| * indicating the open request has failed. It initializes |
| * certain SCB variables and sends a AVDT_CCB_UL_CLOSE_EVT |
| * to the CCB. |
| * |
| * Returns Nothing. |
| * |
| ******************************************************************************/ |
| void avdt_scb_hdl_open_rej(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data) { |
| /* do exactly same as setconfig reject */ |
| avdt_scb_hdl_setconfig_rej(p_scb, p_data); |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avdt_scb_hdl_open_rsp |
| * |
| * Description This function calls avdt_ad_open_req() to initiate |
| * connection of the transport channel for this stream. |
| * |
| * Returns Nothing. |
| * |
| ******************************************************************************/ |
| void avdt_scb_hdl_open_rsp(AvdtpScb* p_scb, UNUSED_ATTR tAVDT_SCB_EVT* p_data) { |
| /* initiate opening of trans channels for this SEID */ |
| p_scb->role = AVDT_OPEN_INT; |
| avdt_ad_open_req(AVDT_CHAN_MEDIA, p_scb->p_ccb, p_scb, AVDT_INT); |
| |
| /* start tc connect timer */ |
| alarm_set_on_mloop(p_scb->transport_channel_timer, |
| AVDT_SCB_TC_CONN_TIMEOUT_MS, |
| avdt_scb_transport_channel_timer_timeout, p_scb); |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avdt_scb_hdl_pkt_no_frag |
| * |
| * Description |
| * |
| * Returns Nothing. |
| * |
| ******************************************************************************/ |
| void avdt_scb_hdl_pkt_no_frag(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data) { |
| uint8_t *p, *p_start; |
| uint8_t o_v, o_p, o_x, o_cc; |
| uint8_t m_pt; |
| uint8_t marker; |
| uint16_t seq; |
| uint32_t time_stamp; |
| uint16_t offset; |
| uint16_t ex_len; |
| uint8_t pad_len = 0; |
| uint16_t len = p_data->p_pkt->len; |
| |
| p = p_start = (uint8_t*)(p_data->p_pkt + 1) + p_data->p_pkt->offset; |
| |
| /* parse media packet header */ |
| offset = 12; |
| // AVDT_MSG_PRS_OCTET1(1) + AVDT_MSG_PRS_M_PT(1) + UINT16(2) + UINT32(4) + 4 |
| if (offset > len) goto length_error; |
| AVDT_MSG_PRS_OCTET1(p, o_v, o_p, o_x, o_cc); |
| AVDT_MSG_PRS_M_PT(p, m_pt, marker); |
| BE_STREAM_TO_UINT16(seq, p); |
| BE_STREAM_TO_UINT32(time_stamp, p); |
| p += 4; |
| |
| /* skip over any csrc's in packet */ |
| offset += o_cc * 4; |
| p += o_cc * 4; |
| |
| /* check for and skip over extension header */ |
| if (o_x) { |
| offset += 4; |
| if (offset > len) goto length_error; |
| p += 2; |
| BE_STREAM_TO_UINT16(ex_len, p); |
| p += ex_len * 4; |
| } |
| |
| if ((p - p_start) >= len) { |
| osi_free_and_reset((void**)&p_data->p_pkt); |
| return; |
| } |
| offset = p - p_start; |
| |
| /* adjust length for any padding at end of packet */ |
| if (o_p) { |
| /* padding length in last byte of packet */ |
| pad_len = *(p_start + len - 1); |
| } |
| |
| /* do sanity check */ |
| if (pad_len >= (len - offset)) { |
| log::warn("Got bad media packet"); |
| osi_free_and_reset((void**)&p_data->p_pkt); |
| } |
| /* adjust offset and length and send it up */ |
| else { |
| p_data->p_pkt->len -= (offset + pad_len); |
| p_data->p_pkt->offset += offset; |
| |
| if (p_scb->stream_config.p_sink_data_cback != NULL) { |
| /* report sequence number */ |
| p_data->p_pkt->layer_specific = seq; |
| (*p_scb->stream_config.p_sink_data_cback)( |
| avdt_scb_to_hdl(p_scb), p_data->p_pkt, time_stamp, |
| (uint8_t)(m_pt | (marker << 7))); |
| } else { |
| osi_free_and_reset((void**)&p_data->p_pkt); |
| } |
| } |
| return; |
| length_error: |
| log::warn("hdl packet length {} too short: must be at least {}", len, offset); |
| osi_free_and_reset((void**)&p_data->p_pkt); |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avdt_scb_hdl_report |
| * |
| * Description |
| * |
| * Returns Nothing. |
| * |
| ******************************************************************************/ |
| uint8_t* avdt_scb_hdl_report(AvdtpScb* p_scb, uint8_t* p, uint16_t len) { |
| uint16_t result = AVDT_SUCCESS; |
| uint8_t* p_start = p; |
| uint32_t ssrc; |
| uint8_t o_v, o_p, o_cc; |
| uint32_t min_len = 0; |
| AVDT_REPORT_TYPE pt; |
| tAVDT_REPORT_DATA report; |
| |
| log::verbose(""); |
| if (p_scb->stream_config.p_report_cback) { |
| /* parse report packet header */ |
| min_len += 8; |
| if (min_len > len) { |
| log::warn("hdl packet length {} too short: must be at least {}", len, |
| min_len); |
| goto avdt_scb_hdl_report_exit; |
| } |
| AVDT_MSG_PRS_RPT_OCTET1(p, o_v, o_p, o_cc); |
| pt = *p++; |
| p += 2; |
| BE_STREAM_TO_UINT32(ssrc, p); |
| |
| switch (pt) { |
| case AVDT_RTCP_PT_SR: /* the packet type - SR (Sender Report) */ |
| min_len += 20; |
| if (min_len > len) { |
| log::warn("hdl packet length {} too short: must be at least {}", len, |
| min_len); |
| goto avdt_scb_hdl_report_exit; |
| } |
| BE_STREAM_TO_UINT32(report.sr.ntp_sec, p); |
| BE_STREAM_TO_UINT32(report.sr.ntp_frac, p); |
| BE_STREAM_TO_UINT32(report.sr.rtp_time, p); |
| BE_STREAM_TO_UINT32(report.sr.pkt_count, p); |
| BE_STREAM_TO_UINT32(report.sr.octet_count, p); |
| break; |
| |
| case AVDT_RTCP_PT_RR: /* the packet type - RR (Receiver Report) */ |
| min_len += 20; |
| if (min_len > len) { |
| log::warn("hdl packet length {} too short: must be at least {}", len, |
| min_len); |
| goto avdt_scb_hdl_report_exit; |
| } |
| report.rr.frag_lost = *p; |
| BE_STREAM_TO_UINT32(report.rr.packet_lost, p); |
| report.rr.packet_lost &= 0xFFFFFF; |
| BE_STREAM_TO_UINT32(report.rr.seq_num_rcvd, p); |
| BE_STREAM_TO_UINT32(report.rr.jitter, p); |
| BE_STREAM_TO_UINT32(report.rr.lsr, p); |
| BE_STREAM_TO_UINT32(report.rr.dlsr, p); |
| break; |
| |
| case AVDT_RTCP_PT_SDES: /* the packet type - SDES (Source Description) */ |
| uint8_t sdes_type; |
| min_len += 1; |
| if (min_len > len) { |
| log::warn("hdl packet length {} too short: must be at least {}", len, |
| min_len); |
| goto avdt_scb_hdl_report_exit; |
| } |
| BE_STREAM_TO_UINT8(sdes_type, p); |
| if (sdes_type == AVDT_RTCP_SDES_CNAME) { |
| uint8_t name_length; |
| min_len += 1; |
| if (min_len > len) { |
| log::warn("hdl packet length {} too short: must be at least {}", |
| len, min_len); |
| goto avdt_scb_hdl_report_exit; |
| } |
| BE_STREAM_TO_UINT8(name_length, p); |
| if (name_length > len - min_len || |
| name_length > AVDT_MAX_CNAME_SIZE) { |
| result = AVDT_BAD_PARAMS; |
| } else { |
| BE_STREAM_TO_ARRAY(p, &(report.cname[0]), name_length); |
| } |
| } else { |
| if (min_len + 1 > len) { |
| log::warn("hdl packet length {} too short: must be at least {}", |
| len, min_len); |
| goto avdt_scb_hdl_report_exit; |
| } |
| log::warn("SDES SSRC=0x{:08x} sc={} {} len={}", ssrc, o_cc, sdes_type, |
| *p); |
| result = AVDT_BUSY; |
| } |
| break; |
| |
| default: |
| log::error("Bad Report pkt - packet type: {}", pt); |
| result = AVDT_BAD_PARAMS; |
| } |
| |
| if (result == AVDT_SUCCESS) |
| (*p_scb->stream_config.p_report_cback)(avdt_scb_to_hdl(p_scb), pt, |
| &report); |
| } |
| avdt_scb_hdl_report_exit: |
| p_start += len; |
| return p_start; |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avdt_scb_hdl_pkt |
| * |
| * Description |
| * |
| * Returns Nothing. |
| * |
| ******************************************************************************/ |
| void avdt_scb_hdl_pkt(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data) { |
| if (p_data->p_pkt->layer_specific == AVDT_CHAN_REPORT) { |
| uint8_t* p = (uint8_t*)(p_data->p_pkt + 1) + p_data->p_pkt->offset; |
| avdt_scb_hdl_report(p_scb, p, p_data->p_pkt->len); |
| osi_free_and_reset((void**)&p_data->p_pkt); |
| } else { |
| avdt_scb_hdl_pkt_no_frag(p_scb, p_data); |
| } |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avdt_scb_drop_pkt |
| * |
| * Description Drop an incoming media packet. This function is called if |
| * a media packet is received in any state besides streaming. |
| * |
| * Returns Nothing. |
| * |
| ******************************************************************************/ |
| void avdt_scb_drop_pkt(UNUSED_ATTR AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data) { |
| log::error("dropped incoming media packet"); |
| osi_free_and_reset((void**)&p_data->p_pkt); |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avdt_scb_hdl_reconfig_cmd |
| * |
| * Description This function calls the application callback function |
| * with a reconfiguration indication. |
| * |
| * Returns Nothing. |
| * |
| ******************************************************************************/ |
| void avdt_scb_hdl_reconfig_cmd(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data) { |
| /* if command not supported */ |
| if (p_scb->stream_config.nsc_mask & AvdtpStreamConfig::AVDT_NSC_RECONFIG) { |
| /* send reject */ |
| p_data->msg.hdr.err_code = AVDT_ERR_NSC; |
| p_data->msg.hdr.err_param = 0; |
| avdt_scb_event(p_scb, AVDT_SCB_API_RECONFIG_RSP_EVT, p_data); |
| } else { |
| /* store requested configuration */ |
| p_scb->req_cfg = *p_data->msg.reconfig_cmd.p_cfg; |
| |
| /* call application callback */ |
| (*p_scb->stream_config.p_avdt_ctrl_cback)( |
| avdt_scb_to_hdl(p_scb), RawAddress::kEmpty, AVDT_RECONFIG_IND_EVT, |
| (tAVDT_CTRL*)&p_data->msg.reconfig_cmd, p_scb->stream_config.scb_index); |
| } |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avdt_scb_hdl_reconfig_rsp |
| * |
| * Description This function calls the application callback function |
| * with a reconfiguration confirm. |
| * |
| * Returns Nothing. |
| * |
| ******************************************************************************/ |
| void avdt_scb_hdl_reconfig_rsp(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data) { |
| if (p_data->msg.hdr.err_code == 0) { |
| /* store new configuration */ |
| if (p_scb->req_cfg.num_codec > 0) { |
| p_scb->curr_cfg.num_codec = p_scb->req_cfg.num_codec; |
| memcpy(p_scb->curr_cfg.codec_info, p_scb->req_cfg.codec_info, |
| AVDT_CODEC_SIZE); |
| } |
| if (p_scb->req_cfg.num_protect > 0) { |
| p_scb->curr_cfg.num_protect = p_scb->req_cfg.num_protect; |
| memcpy(p_scb->curr_cfg.protect_info, p_scb->req_cfg.protect_info, |
| AVDT_PROTECT_SIZE); |
| } |
| } |
| |
| p_data->msg.svccap.p_cfg = &p_scb->curr_cfg; |
| |
| /* call application callback */ |
| (*p_scb->stream_config.p_avdt_ctrl_cback)( |
| avdt_scb_to_hdl(p_scb), RawAddress::kEmpty, AVDT_RECONFIG_CFM_EVT, |
| (tAVDT_CTRL*)&p_data->msg.svccap, p_scb->stream_config.scb_index); |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avdt_scb_hdl_security_cmd |
| * |
| * Description This function calls the application callback with a |
| * security indication. |
| * |
| * Returns Nothing. |
| * |
| ******************************************************************************/ |
| void avdt_scb_hdl_security_cmd(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data) { |
| /* if command not supported */ |
| if (p_scb->stream_config.nsc_mask & AvdtpStreamConfig::AVDT_NSC_SECURITY) { |
| /* send reject */ |
| p_data->msg.hdr.err_code = AVDT_ERR_NSC; |
| avdt_scb_event(p_scb, AVDT_SCB_API_SECURITY_RSP_EVT, p_data); |
| } else { |
| /* call application callback */ |
| (*p_scb->stream_config.p_avdt_ctrl_cback)( |
| avdt_scb_to_hdl(p_scb), RawAddress::kEmpty, AVDT_SECURITY_IND_EVT, |
| (tAVDT_CTRL*)&p_data->msg.security_cmd, p_scb->stream_config.scb_index); |
| } |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avdt_scb_hdl_security_rsp |
| * |
| * Description This function calls the application callback with a |
| * security confirm. |
| * |
| * Returns Nothing. |
| * |
| ******************************************************************************/ |
| void avdt_scb_hdl_security_rsp(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data) { |
| /* call application callback */ |
| (*p_scb->stream_config.p_avdt_ctrl_cback)( |
| avdt_scb_to_hdl(p_scb), RawAddress::kEmpty, AVDT_SECURITY_CFM_EVT, |
| (tAVDT_CTRL*)&p_data->msg.security_cmd, p_scb->stream_config.scb_index); |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avdt_scb_hdl_setconfig_cmd |
| * |
| * Description This function marks the SCB as in use and copies the |
| * configuration and peer SEID to the SCB. It then calls |
| * the application callback with a configuration indication. |
| * |
| * Returns Nothing. |
| * |
| ******************************************************************************/ |
| void avdt_scb_hdl_setconfig_cmd(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data) { |
| log::verbose("p_scb->in_use={} p_avdt_scb={} scb_index={}", p_scb->in_use, |
| fmt::ptr(p_scb), p_scb->stream_config.scb_index); |
| |
| if (!p_scb->in_use) { |
| log::verbose( |
| "codec: {}", |
| A2DP_CodecInfoString(p_scb->stream_config.cfg.codec_info).c_str()); |
| log::verbose( |
| "codec: {}", |
| A2DP_CodecInfoString(p_data->msg.config_cmd.p_cfg->codec_info).c_str()); |
| AvdtpSepConfig* p_cfg = p_data->msg.config_cmd.p_cfg; |
| if (A2DP_GetCodecType(p_scb->stream_config.cfg.codec_info) == |
| A2DP_GetCodecType(p_cfg->codec_info)) { |
| /* copy info to scb */ |
| AvdtpCcb* p_ccb = avdt_ccb_by_idx(p_data->msg.config_cmd.hdr.ccb_idx); |
| if (p_scb->p_ccb != p_ccb) { |
| log::error( |
| "mismatch in AVDTP SCB/CCB state: (p_scb->p_ccb={} != p_ccb={}): " |
| "p_scb={} scb_handle={} ccb_idx={}", |
| fmt::ptr(p_scb->p_ccb), fmt::ptr(p_ccb), fmt::ptr(p_scb), |
| p_scb->ScbHandle(), p_data->msg.config_cmd.hdr.ccb_idx); |
| avdt_scb_rej_not_in_use(p_scb, p_data); |
| return; |
| } |
| /* set sep as in use */ |
| p_scb->in_use = true; |
| |
| p_scb->peer_seid = p_data->msg.config_cmd.int_seid; |
| p_scb->req_cfg = *p_cfg; |
| /* call app callback */ |
| /* handle of scb- which is same as sep handle of bta_av_cb.p_scb*/ |
| (*p_scb->stream_config.p_avdt_ctrl_cback)( |
| avdt_scb_to_hdl(p_scb), |
| p_scb->p_ccb ? p_scb->p_ccb->peer_addr : RawAddress::kEmpty, |
| AVDT_CONFIG_IND_EVT, (tAVDT_CTRL*)&p_data->msg.config_cmd, |
| p_scb->stream_config.scb_index); |
| } else { |
| p_data->msg.hdr.err_code = AVDT_ERR_UNSUP_CFG; |
| p_data->msg.hdr.err_param = 0; |
| avdt_msg_send_rej(avdt_ccb_by_idx(p_data->msg.hdr.ccb_idx), |
| p_data->msg.hdr.sig_id, &p_data->msg); |
| } |
| } else { |
| log::verbose("calling avdt_scb_rej_in_use()"); |
| avdt_scb_rej_in_use(p_scb, p_data); |
| } |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avdt_scb_hdl_setconfig_rej |
| * |
| * Description This function marks the SCB as not in use and calls the |
| * application callback with an open confirm indicating |
| * failure. |
| * |
| * Returns Nothing. |
| * |
| ******************************************************************************/ |
| void avdt_scb_hdl_setconfig_rej(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data) { |
| /* clear scb variables */ |
| avdt_scb_clr_vars(p_scb, p_data); |
| |
| /* tell ccb we're done with signaling channel */ |
| avdt_ccb_event(avdt_ccb_by_idx(p_data->msg.hdr.ccb_idx), |
| AVDT_CCB_UL_CLOSE_EVT, NULL); |
| |
| /* call application callback */ |
| (*p_scb->stream_config.p_avdt_ctrl_cback)( |
| avdt_scb_to_hdl(p_scb), RawAddress::kEmpty, AVDT_OPEN_CFM_EVT, |
| (tAVDT_CTRL*)&p_data->msg.hdr, p_scb->stream_config.scb_index); |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avdt_scb_snd_snk_delay_rpt_req |
| * |
| * Description This function sends the delay report request once it is sink |
| * |
| * Returns Nothing. |
| * |
| ******************************************************************************/ |
| void avdt_scb_snd_snk_delay_rpt_req(AvdtpScb* p_scb, |
| UNUSED_ATTR tAVDT_SCB_EVT* p_data) { |
| if (p_scb->p_ccb == NULL) { |
| return; |
| } |
| |
| // In sink mode, report a fixed delay value when this device is the sink |
| // side. Delay value in this function is in unit of 1/10ms. |
| if (p_scb->stream_config.tsep != AVDT_TSEP_SNK) { |
| return; |
| } |
| |
| tAVDT_SCB_EVT evt; |
| evt.apidelay.hdr.seid = p_scb->peer_seid; |
| evt.apidelay.delay = AVDT_SINK_DELAY_MS * 10; |
| avdt_scb_event(p_scb, AVDT_SCB_API_DELAY_RPT_REQ_EVT, &evt); |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avdt_scb_hdl_setconfig_rsp |
| * |
| * Description This function sends the SCB an AVDT_SCB_API_OPEN_REQ_EVT |
| * to initiate sending of an open command message. |
| * |
| * This function sends the SCB an AVDT_SCB_API_DELAY_RPT_REQ_EVT |
| * to initiate sending of delay report command message only |
| * when the endpoint takes sink role. |
| * |
| * Returns Nothing. |
| * |
| ******************************************************************************/ |
| void avdt_scb_hdl_setconfig_rsp(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data) { |
| tAVDT_EVT_HDR single; |
| |
| if (p_scb->p_ccb != NULL) { |
| /* save configuration */ |
| p_scb->curr_cfg = p_scb->req_cfg; |
| |
| // In sink mode, report delay value when this device initiates the connection. |
| // Delay reporting is sent before open request (i.e., in configured state). |
| avdt_scb_snd_snk_delay_rpt_req(p_scb, p_data); |
| |
| /* initiate open */ |
| single.seid = p_scb->peer_seid; |
| tAVDT_SCB_EVT avdt_scb_evt; |
| avdt_scb_evt.msg.single = single; |
| avdt_scb_event(p_scb, AVDT_SCB_API_OPEN_REQ_EVT, &avdt_scb_evt); |
| } |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avdt_scb_hdl_start_cmd |
| * |
| * Description This function calls the application callback with a |
| * start indication. |
| * |
| * Returns Nothing. |
| * |
| ******************************************************************************/ |
| void avdt_scb_hdl_start_cmd(AvdtpScb* p_scb, |
| UNUSED_ATTR tAVDT_SCB_EVT* p_data) { |
| (*p_scb->stream_config.p_avdt_ctrl_cback)( |
| avdt_scb_to_hdl(p_scb), |
| p_scb->p_ccb ? p_scb->p_ccb->peer_addr : RawAddress::kEmpty, |
| AVDT_START_IND_EVT, NULL, p_scb->stream_config.scb_index); |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avdt_scb_hdl_start_rsp |
| * |
| * Description This function calls the application callback with a |
| * start confirm. |
| * |
| * Returns Nothing. |
| * |
| ******************************************************************************/ |
| void avdt_scb_hdl_start_rsp(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data) { |
| (*p_scb->stream_config.p_avdt_ctrl_cback)( |
| avdt_scb_to_hdl(p_scb), |
| p_scb->p_ccb ? p_scb->p_ccb->peer_addr : RawAddress::kEmpty, |
| AVDT_START_CFM_EVT, (tAVDT_CTRL*)&p_data->msg.hdr, |
| p_scb->stream_config.scb_index); |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avdt_scb_hdl_suspend_cmd |
| * |
| * Description This function calls the application callback with a suspend |
| * indication. |
| * |
| * Returns Nothing. |
| * |
| ******************************************************************************/ |
| void avdt_scb_hdl_suspend_cmd(AvdtpScb* p_scb, |
| UNUSED_ATTR tAVDT_SCB_EVT* p_data) { |
| (*p_scb->stream_config.p_avdt_ctrl_cback)( |
| avdt_scb_to_hdl(p_scb), |
| p_scb->p_ccb ? p_scb->p_ccb->peer_addr : RawAddress::kEmpty, |
| AVDT_SUSPEND_IND_EVT, NULL, p_scb->stream_config.scb_index); |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avdt_scb_hdl_suspend_rsp |
| * |
| * Description This function calls the application callback with a suspend |
| * confirm. |
| * |
| * Returns Nothing. |
| * |
| ******************************************************************************/ |
| void avdt_scb_hdl_suspend_rsp(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data) { |
| (*p_scb->stream_config.p_avdt_ctrl_cback)( |
| avdt_scb_to_hdl(p_scb), |
| p_scb->p_ccb ? p_scb->p_ccb->peer_addr : RawAddress::kEmpty, |
| AVDT_SUSPEND_CFM_EVT, (tAVDT_CTRL*)&p_data->msg.hdr, |
| p_scb->stream_config.scb_index); |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avdt_scb_hdl_tc_close |
| * |
| * Description This function is called when the transport channel is |
| * closed. It marks the SCB as not in use and |
| * initializes certain SCB parameters. It then sends |
| * an AVDT_CCB_UL_CLOSE_EVT to the CCB if the SCB |
| * initiated the close. It then checks to see if the SCB |
| * is to be removed. If it is it deallocates the SCB. |
| * Finally, it calls the application callback with a close |
| * indication. |
| * |
| * Returns Nothing. |
| * |
| ******************************************************************************/ |
| void avdt_scb_hdl_tc_close(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data) { |
| uint8_t hdl = avdt_scb_to_hdl(p_scb); |
| tAVDT_CTRL_CBACK* p_avdt_ctrl_cback = p_scb->stream_config.p_avdt_ctrl_cback; |
| tAVDT_CTRL avdt_ctrl; |
| uint8_t event; |
| AvdtpCcb* p_ccb = p_scb->p_ccb; |
| RawAddress remote_addr = p_ccb->peer_addr; |
| uint8_t scb_index = p_scb->stream_config.scb_index; |
| |
| /* set up hdr */ |
| avdt_ctrl.hdr.err_code = p_scb->close_code; |
| |
| /* clear sep variables */ |
| avdt_scb_clr_vars(p_scb, p_data); |
| p_scb->media_seq = 0; |
| p_scb->cong = false; |
| |
| /* free pkt we're holding, if any */ |
| osi_free_and_reset((void**)&p_scb->p_pkt); |
| |
| alarm_cancel(p_scb->transport_channel_timer); |
| |
| if ((p_scb->role == AVDT_CLOSE_INT) || (p_scb->role == AVDT_OPEN_INT)) { |
| /* tell ccb we're done with signaling channel */ |
| avdt_ccb_event(p_ccb, AVDT_CCB_UL_CLOSE_EVT, NULL); |
| } |
| event = |
| (p_scb->role == AVDT_CLOSE_INT) ? AVDT_CLOSE_CFM_EVT : AVDT_CLOSE_IND_EVT; |
| p_scb->role = AVDT_CLOSE_ACP; |
| |
| if (p_scb->remove) { |
| avdt_scb_dealloc(p_scb, NULL); |
| } |
| |
| /* call app callback */ |
| (*p_avdt_ctrl_cback)(hdl, remote_addr, event, &avdt_ctrl, scb_index); |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avdt_scb_snd_delay_rpt_req |
| * |
| * Description This function calls the application callback with a delay |
| * report. |
| * |
| * Returns Nothing. |
| * |
| ******************************************************************************/ |
| void avdt_scb_snd_delay_rpt_req(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data) { |
| if (p_scb->stream_config.cfg.psc_mask & AVDT_PSC_DELAY_RPT) { |
| avdt_msg_send_cmd(p_scb->p_ccb, p_scb, AVDT_SIG_DELAY_RPT, |
| (tAVDT_MSG*)&p_data->apidelay); |
| } |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avdt_scb_hdl_delay_rpt_cmd |
| * |
| * Description This function calls the application callback with a delay |
| * report. |
| * |
| * Returns Nothing. |
| * |
| ******************************************************************************/ |
| void avdt_scb_hdl_delay_rpt_cmd(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data) { |
| (*p_scb->stream_config.p_avdt_ctrl_cback)( |
| avdt_scb_to_hdl(p_scb), |
| p_scb->p_ccb ? p_scb->p_ccb->peer_addr : RawAddress::kEmpty, |
| AVDT_DELAY_REPORT_EVT, (tAVDT_CTRL*)&p_data->msg.hdr, |
| p_scb->stream_config.scb_index); |
| |
| if (p_scb->p_ccb) |
| avdt_msg_send_rsp(p_scb->p_ccb, AVDT_SIG_DELAY_RPT, &p_data->msg); |
| else |
| avdt_scb_rej_not_in_use(p_scb, p_data); |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avdt_scb_hdl_delay_rpt_rsp |
| * |
| * Description This function calls the application callback with a delay |
| * report. |
| * |
| * Returns Nothing. |
| * |
| ******************************************************************************/ |
| void avdt_scb_hdl_delay_rpt_rsp(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data) { |
| (*p_scb->stream_config.p_avdt_ctrl_cback)( |
| avdt_scb_to_hdl(p_scb), |
| p_scb->p_ccb ? p_scb->p_ccb->peer_addr : RawAddress::kEmpty, |
| AVDT_DELAY_REPORT_CFM_EVT, (tAVDT_CTRL*)&p_data->msg.hdr, |
| p_scb->stream_config.scb_index); |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avdt_scb_hdl_tc_close_sto |
| * |
| * Description This function is called when a channel is closed in OPEN |
| * state. Check the channel type and process accordingly. |
| * |
| * Returns Nothing. |
| * |
| ******************************************************************************/ |
| void avdt_scb_hdl_tc_close_sto(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data) { |
| tAVDT_CTRL avdt_ctrl; |
| /* AVDT_CHAN_SIG does not visit this action */ |
| if (p_data && p_data->close.type != AVDT_CHAN_MEDIA) { |
| /* it's reporting or recovery channel, |
| * the channel close in open state means the peer does not support it */ |
| if (p_data->close.old_tc_state == AVDT_AD_ST_OPEN) { |
| avdt_ctrl.hdr.err_code = 0; |
| avdt_ctrl.hdr.err_param = 0; |
| /* call app callback */ |
| (*p_scb->stream_config.p_avdt_ctrl_cback)( |
| avdt_scb_to_hdl(p_scb), |
| p_scb->p_ccb ? p_scb->p_ccb->peer_addr : RawAddress::kEmpty, |
| AVDT_REPORT_DISCONN_EVT, &avdt_ctrl, p_scb->stream_config.scb_index); |
| } |
| } else { |
| /* must be in OPEN state. need to go back to idle */ |
| avdt_scb_event(p_scb, AVDT_SCB_MSG_ABORT_RSP_EVT, NULL); |
| avdt_scb_hdl_tc_close(p_scb, p_data); |
| } |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avdt_scb_hdl_tc_open |
| * |
| * Description This function is called when the transport channel is |
| * opened while in the opening state. It calls the |
| * application callback with an open indication or open |
| * confirm depending on who initiated the open procedure. |
| * |
| * Returns Nothing. |
| * |
| ******************************************************************************/ |
| void avdt_scb_hdl_tc_open(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data) { |
| uint8_t event; |
| uint8_t role; |
| |
| alarm_cancel(p_scb->transport_channel_timer); |
| |
| event = |
| (p_scb->role == AVDT_OPEN_INT) ? AVDT_OPEN_CFM_EVT : AVDT_OPEN_IND_EVT; |
| p_data->open.hdr.err_code = 0; |
| |
| log::verbose("psc_mask: cfg: 0x{:x}, req:0x{:x}, cur: 0x{:x}", |
| p_scb->stream_config.cfg.psc_mask, p_scb->req_cfg.psc_mask, |
| p_scb->curr_cfg.psc_mask); |
| if (p_scb->curr_cfg.psc_mask & AVDT_PSC_REPORT) { |
| /* open the reporting channel, if both devices support it */ |
| role = (p_scb->role == AVDT_OPEN_INT) ? AVDT_INT : AVDT_ACP; |
| avdt_ad_open_req(AVDT_CHAN_REPORT, p_scb->p_ccb, p_scb, role); |
| } |
| |
| /* call app callback */ |
| (*p_scb->stream_config.p_avdt_ctrl_cback)( |
| avdt_scb_to_hdl(p_scb), |
| p_scb->p_ccb ? p_scb->p_ccb->peer_addr : RawAddress::kEmpty, event, |
| (tAVDT_CTRL*)&p_data->open, p_scb->stream_config.scb_index); |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avdt_scb_hdl_tc_open_sto |
| * |
| * Description This function is called when the transport channel is |
| * opened while in the opening state. It calls the |
| * application callback with an open indication or open |
| * confirm depending on who initiated the open procedure. |
| * |
| * Returns Nothing. |
| * |
| ******************************************************************************/ |
| void avdt_scb_hdl_tc_open_sto(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data) { |
| tAVDT_CTRL avdt_ctrl; |
| /* open reporting channel here, when it is implemented */ |
| |
| /* call app callback */ |
| if (p_data->open.hdr.err_code == AVDT_CHAN_REPORT) { |
| avdt_ctrl.hdr.err_code = 0; |
| avdt_ctrl.hdr.err_param = 1; |
| (*p_scb->stream_config.p_avdt_ctrl_cback)( |
| avdt_scb_to_hdl(p_scb), |
| p_scb->p_ccb ? p_scb->p_ccb->peer_addr : RawAddress::kEmpty, |
| AVDT_REPORT_CONN_EVT, &avdt_ctrl, p_scb->stream_config.scb_index); |
| } |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avdt_scb_hdl_write_req |
| * |
| * Description This function frees the media packet currently stored in |
| * the SCB, if any. Then it builds a new media packet from |
| * with the passed in buffer and stores it in the SCB. |
| * |
| * Returns Nothing. |
| * |
| ******************************************************************************/ |
| void avdt_scb_hdl_write_req(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data) { |
| uint8_t* p; |
| uint32_t ssrc; |
| bool add_rtp_header = !(p_data->apiwrite.opt & AVDT_DATA_OPT_NO_RTP); |
| |
| /* free packet we're holding, if any; to be replaced with new */ |
| if (p_scb->p_pkt != NULL) { |
| /* this shouldn't be happening */ |
| log::warn("Dropped media packet; congested"); |
| } |
| osi_free_and_reset((void**)&p_scb->p_pkt); |
| |
| /* Recompute only if the RTP header wasn't disabled by the API */ |
| if (add_rtp_header) { |
| bool is_content_protection = (p_scb->curr_cfg.num_protect > 0); |
| add_rtp_header = |
| A2DP_UsesRtpHeader(is_content_protection, p_scb->curr_cfg.codec_info); |
| } |
| |
| /* Build a media packet, and add an RTP header if required. */ |
| if (add_rtp_header) { |
| if (p_data->apiwrite.p_buf->offset < AVDT_MEDIA_HDR_SIZE) { |
| return; |
| } |
| |
| ssrc = avdt_scb_gen_ssrc(p_scb); |
| |
| p_data->apiwrite.p_buf->len += AVDT_MEDIA_HDR_SIZE; |
| p_data->apiwrite.p_buf->offset -= AVDT_MEDIA_HDR_SIZE; |
| p_scb->media_seq++; |
| p = (uint8_t*)(p_data->apiwrite.p_buf + 1) + p_data->apiwrite.p_buf->offset; |
| |
| UINT8_TO_BE_STREAM(p, AVDT_MEDIA_OCTET1); |
| UINT8_TO_BE_STREAM(p, p_data->apiwrite.m_pt); |
| UINT16_TO_BE_STREAM(p, p_scb->media_seq); |
| UINT32_TO_BE_STREAM(p, p_data->apiwrite.time_stamp); |
| UINT32_TO_BE_STREAM(p, ssrc); |
| } |
| |
| /* store it */ |
| p_scb->p_pkt = p_data->apiwrite.p_buf; |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avdt_scb_snd_abort_req |
| * |
| * Description This function sends an abort command message. |
| * |
| * Returns Nothing. |
| * |
| ******************************************************************************/ |
| void avdt_scb_snd_abort_req(AvdtpScb* p_scb, |
| UNUSED_ATTR tAVDT_SCB_EVT* p_data) { |
| tAVDT_EVT_HDR hdr; |
| |
| log::verbose("p_scb->p_ccb={}", fmt::ptr(p_scb->p_ccb)); |
| |
| if (p_scb->p_ccb != NULL) { |
| p_scb->role = AVDT_CLOSE_INT; |
| |
| hdr.seid = p_scb->peer_seid; |
| |
| tAVDT_MSG avdt_msg; |
| avdt_msg.hdr = hdr; |
| avdt_msg_send_cmd(p_scb->p_ccb, p_scb, AVDT_SIG_ABORT, &avdt_msg); |
| } |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avdt_scb_snd_abort_rsp |
| * |
| * Description This function sends an abort response message. |
| * |
| * Returns Nothing. |
| * |
| ******************************************************************************/ |
| void avdt_scb_snd_abort_rsp(UNUSED_ATTR AvdtpScb* p_scb, |
| tAVDT_SCB_EVT* p_data) { |
| avdt_msg_send_rsp(avdt_ccb_by_idx(p_data->msg.hdr.ccb_idx), AVDT_SIG_ABORT, |
| &p_data->msg); |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avdt_scb_snd_close_req |
| * |
| * Description This function sends a close command message. |
| * |
| * Returns Nothing. |
| * |
| ******************************************************************************/ |
| void avdt_scb_snd_close_req(AvdtpScb* p_scb, |
| UNUSED_ATTR tAVDT_SCB_EVT* p_data) { |
| tAVDT_EVT_HDR hdr; |
| |
| p_scb->role = AVDT_CLOSE_INT; |
| |
| hdr.seid = p_scb->peer_seid; |
| |
| tAVDT_MSG avdt_msg; |
| avdt_msg.hdr = hdr; |
| avdt_msg_send_cmd(p_scb->p_ccb, p_scb, AVDT_SIG_CLOSE, &avdt_msg); |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avdt_scb_snd_stream_close |
| * |
| * Description This function sends a close command message. |
| * |
| * Returns Nothing. |
| * |
| ******************************************************************************/ |
| void avdt_scb_snd_stream_close(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data) { |
| osi_free_and_reset((void**)&p_scb->p_pkt); |
| avdt_scb_snd_close_req(p_scb, p_data); |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avdt_scb_snd_close_rsp |
| * |
| * Description This function sends a close response message. |
| * |
| * Returns Nothing. |
| * |
| ******************************************************************************/ |
| void avdt_scb_snd_close_rsp(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data) { |
| avdt_msg_send_rsp(p_scb->p_ccb, AVDT_SIG_CLOSE, &p_data->msg); |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avdt_scb_snd_getconfig_req |
| * |
| * Description This function sends a get configuration command message. |
| * |
| * Returns Nothing. |
| * |
| ******************************************************************************/ |
| void avdt_scb_snd_getconfig_req(AvdtpScb* p_scb, |
| UNUSED_ATTR tAVDT_SCB_EVT* p_data) { |
| tAVDT_EVT_HDR hdr; |
| |
| hdr.seid = p_scb->peer_seid; |
| |
| tAVDT_MSG avdt_msg; |
| avdt_msg.hdr = hdr; |
| avdt_msg_send_cmd(p_scb->p_ccb, p_scb, AVDT_SIG_GETCONFIG, &avdt_msg); |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avdt_scb_snd_getconfig_rsp |
| * |
| * Description This function sends a get configuration response message. |
| * |
| * Returns Nothing. |
| * |
| ******************************************************************************/ |
| void avdt_scb_snd_getconfig_rsp(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data) { |
| avdt_msg_send_rsp(p_scb->p_ccb, AVDT_SIG_GETCONFIG, &p_data->msg); |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avdt_scb_snd_open_req |
| * |
| * Description This function sends an open command message. |
| * |
| * Returns Nothing. |
| * |
| ******************************************************************************/ |
| void avdt_scb_snd_open_req(AvdtpScb* p_scb, UNUSED_ATTR tAVDT_SCB_EVT* p_data) { |
| tAVDT_EVT_HDR hdr; |
| |
| hdr.seid = p_scb->peer_seid; |
| |
| tAVDT_MSG avdt_msg; |
| avdt_msg.hdr = hdr; |
| avdt_msg_send_cmd(p_scb->p_ccb, p_scb, AVDT_SIG_OPEN, &avdt_msg); |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avdt_scb_snd_open_rsp |
| * |
| * Description This function sends an open response message. It also |
| * calls avdt_ad_open_req() to accept a transport channel |
| * connection. |
| * |
| * Returns Nothing. |
| * |
| ******************************************************************************/ |
| void avdt_scb_snd_open_rsp(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data) { |
| /* notify adaption that we're waiting for transport channel open */ |
| p_scb->role = AVDT_OPEN_ACP; |
| avdt_ad_open_req(AVDT_CHAN_MEDIA, p_scb->p_ccb, p_scb, AVDT_ACP); |
| |
| /* send response */ |
| avdt_msg_send_rsp(p_scb->p_ccb, AVDT_SIG_OPEN, &p_data->msg); |
| |
| alarm_set_on_mloop(p_scb->transport_channel_timer, |
| AVDT_SCB_TC_CONN_TIMEOUT_MS, |
| avdt_scb_transport_channel_timer_timeout, p_scb); |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avdt_scb_snd_reconfig_req |
| * |
| * Description This function stores the configuration parameters in the |
| * SCB and sends a reconfiguration command message. |
| * |
| * Returns Nothing. |
| * |
| ******************************************************************************/ |
| void avdt_scb_snd_reconfig_req(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data) { |
| log::verbose("p_scb->peer_seid={} p_data->msg.hdr.seid={}", p_scb->peer_seid, |
| p_data->msg.hdr.seid); |
| log::verbose( |
| "codec: {}", |
| A2DP_CodecInfoString(p_data->msg.config_cmd.p_cfg->codec_info).c_str()); |
| |
| p_scb->req_cfg = *p_data->msg.config_cmd.p_cfg; |
| p_data->msg.hdr.seid = p_scb->peer_seid; |
| avdt_msg_send_cmd(p_scb->p_ccb, p_scb, AVDT_SIG_RECONFIG, &p_data->msg); |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avdt_scb_snd_reconfig_rsp |
| * |
| * Description This function stores the configuration parameters in the |
| * SCB and sends a reconfiguration response message. |
| * |
| * Returns Nothing. |
| * |
| ******************************************************************************/ |
| void avdt_scb_snd_reconfig_rsp(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data) { |
| if (p_data->msg.hdr.err_code == 0) { |
| /* store new configuration */ |
| if (p_scb->req_cfg.num_codec > 0) { |
| p_scb->curr_cfg.num_codec = p_scb->req_cfg.num_codec; |
| memcpy(p_scb->curr_cfg.codec_info, p_scb->req_cfg.codec_info, |
| AVDT_CODEC_SIZE); |
| } |
| if (p_scb->req_cfg.num_protect > 0) { |
| p_scb->curr_cfg.num_protect = p_scb->req_cfg.num_protect; |
| memcpy(p_scb->curr_cfg.protect_info, p_scb->req_cfg.protect_info, |
| AVDT_PROTECT_SIZE); |
| } |
| |
| /* send response */ |
| avdt_msg_send_rsp(p_scb->p_ccb, AVDT_SIG_RECONFIG, &p_data->msg); |
| } else { |
| /* send reject */ |
| avdt_msg_send_rej(p_scb->p_ccb, AVDT_SIG_RECONFIG, &p_data->msg); |
| } |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avdt_scb_snd_security_req |
| * |
| * Description This function sends a security command message. |
| * |
| * Returns Nothing. |
| * |
| ******************************************************************************/ |
| void avdt_scb_snd_security_req(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data) { |
| p_data->msg.hdr.seid = p_scb->peer_seid; |
| avdt_msg_send_cmd(p_scb->p_ccb, p_scb, AVDT_SIG_SECURITY, &p_data->msg); |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avdt_scb_snd_security_rsp |
| * |
| * Description This function sends a security response message. |
| * |
| * Returns Nothing. |
| * |
| ******************************************************************************/ |
| void avdt_scb_snd_security_rsp(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data) { |
| if (p_data->msg.hdr.err_code == 0) { |
| avdt_msg_send_rsp(p_scb->p_ccb, AVDT_SIG_SECURITY, &p_data->msg); |
| } else { |
| avdt_msg_send_rej(p_scb->p_ccb, AVDT_SIG_SECURITY, &p_data->msg); |
| } |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avdt_scb_snd_setconfig_rej |
| * |
| * Description This function marks the SCB as not in use and sends a |
| * set configuration reject message. |
| * |
| * Returns Nothing. |
| * |
| ******************************************************************************/ |
| void avdt_scb_snd_setconfig_rej(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data) { |
| if (p_scb->p_ccb != NULL) { |
| avdt_msg_send_rej(p_scb->p_ccb, AVDT_SIG_SETCONFIG, &p_data->msg); |
| |
| /* clear scb variables */ |
| avdt_scb_clr_vars(p_scb, p_data); |
| } |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avdt_scb_snd_setconfig_req |
| * |
| * Description This function marks the SCB as in use and copies the |
| * configuration parameters to the SCB. Then the function |
| * sends a set configuration command message and initiates |
| * opening of the signaling channel. |
| * |
| * Returns Nothing. |
| * |
| ******************************************************************************/ |
| void avdt_scb_snd_setconfig_req(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data) { |
| log::verbose( |
| "codec: {}", |
| A2DP_CodecInfoString(p_data->msg.config_cmd.p_cfg->codec_info).c_str()); |
| |
| /* copy API parameters to scb, set scb as in use */ |
| |
| AvdtpCcb* p_ccb = avdt_ccb_by_idx(p_data->msg.config_cmd.hdr.ccb_idx); |
| if (p_scb->p_ccb != p_ccb) { |
| log::error( |
| "mismatch in AVDTP SCB/CCB state: (p_scb->p_ccb={} != p_ccb={}): " |
| "p_scb={} scb_handle={} ccb_idx={}", |
| fmt::ptr(p_scb->p_ccb), fmt::ptr(p_ccb), fmt::ptr(p_scb), |
| p_scb->ScbHandle(), p_data->msg.config_cmd.hdr.ccb_idx); |
| avdt_scb_rej_not_in_use(p_scb, p_data); |
| return; |
| } |
| p_scb->in_use = true; |
| p_scb->peer_seid = p_data->msg.config_cmd.hdr.seid; |
| p_scb->req_cfg = *p_data->msg.config_cmd.p_cfg; |
| |
| avdt_msg_send_cmd(p_scb->p_ccb, p_scb, AVDT_SIG_SETCONFIG, &p_data->msg); |
| |
| /* tell ccb to open channel */ |
| avdt_ccb_event(p_scb->p_ccb, AVDT_CCB_UL_OPEN_EVT, NULL); |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avdt_scb_snd_setconfig_rsp |
| * |
| * Description This function copies the requested configuration into the |
| * current configuration and sends a set configuration |
| * response message. |
| * |
| * Returns Nothing. |
| * |
| ******************************************************************************/ |
| void avdt_scb_snd_setconfig_rsp(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data) { |
| if (p_scb->p_ccb != NULL) { |
| p_scb->curr_cfg = p_scb->req_cfg; |
| |
| avdt_msg_send_rsp(p_scb->p_ccb, AVDT_SIG_SETCONFIG, &p_data->msg); |
| } |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avdt_scb_snd_tc_close |
| * |
| * Description This function calls avdt_ad_close_req() to close the |
| * transport channel for this SCB. |
| * |
| * Returns Nothing. |
| * |
| ******************************************************************************/ |
| void avdt_scb_snd_tc_close(AvdtpScb* p_scb, UNUSED_ATTR tAVDT_SCB_EVT* p_data) { |
| if (p_scb->curr_cfg.psc_mask & AVDT_PSC_REPORT) |
| avdt_ad_close_req(AVDT_CHAN_REPORT, p_scb->p_ccb, p_scb); |
| avdt_ad_close_req(AVDT_CHAN_MEDIA, p_scb->p_ccb, p_scb); |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avdt_scb_cb_err |
| * |
| * Description This function calls the application callback function |
| * indicating an error. |
| * |
| * Returns Nothing. |
| * |
| ******************************************************************************/ |
| void avdt_scb_cb_err(AvdtpScb* p_scb, UNUSED_ATTR tAVDT_SCB_EVT* p_data) { |
| tAVDT_CTRL avdt_ctrl; |
| |
| /* set error code and parameter */ |
| avdt_ctrl.hdr.err_code = AVDT_ERR_BAD_STATE; |
| avdt_ctrl.hdr.err_param = 0; |
| |
| /* call callback, using lookup table to get callback event */ |
| (*p_scb->stream_config.p_avdt_ctrl_cback)( |
| avdt_scb_to_hdl(p_scb), RawAddress::kEmpty, |
| avdt_scb_cback_evt[p_scb->curr_evt], &avdt_ctrl, |
| p_scb->stream_config.scb_index); |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avdt_scb_cong_state |
| * |
| * Description This function sets the congestion state of the SCB media |
| * transport channel. |
| * |
| * Returns Nothing. |
| * |
| ******************************************************************************/ |
| void avdt_scb_cong_state(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data) { |
| p_scb->cong = p_data->llcong; |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avdt_scb_rej_state |
| * |
| * Description This function sends a reject message to the peer indicating |
| * incorrect state for the received command message. |
| * |
| * Returns Nothing. |
| * |
| ******************************************************************************/ |
| void avdt_scb_rej_state(UNUSED_ATTR AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data) { |
| p_data->msg.hdr.err_code = AVDT_ERR_BAD_STATE; |
| p_data->msg.hdr.err_param = 0; |
| avdt_msg_send_rej(avdt_ccb_by_idx(p_data->msg.hdr.ccb_idx), |
| p_data->msg.hdr.sig_id, &p_data->msg); |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avdt_scb_rej_in_use |
| * |
| * Description This function sends a reject message to the peer indicating |
| * the stream is in use. |
| * |
| * Returns Nothing. |
| * |
| ******************************************************************************/ |
| void avdt_scb_rej_in_use(UNUSED_ATTR AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data) { |
| p_data->msg.hdr.err_code = AVDT_ERR_IN_USE; |
| p_data->msg.hdr.err_param = 0; |
| avdt_msg_send_rej(avdt_ccb_by_idx(p_data->msg.hdr.ccb_idx), |
| p_data->msg.hdr.sig_id, &p_data->msg); |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avdt_scb_rej_not_in_use |
| * |
| * Description This function sends a reject message to the peer indicating |
| * the stream is in use. |
| * |
| * Returns Nothing. |
| * |
| ******************************************************************************/ |
| void avdt_scb_rej_not_in_use(UNUSED_ATTR AvdtpScb* p_scb, |
| tAVDT_SCB_EVT* p_data) { |
| p_data->msg.hdr.err_code = AVDT_ERR_NOT_IN_USE; |
| p_data->msg.hdr.err_param = 0; |
| avdt_msg_send_rej(avdt_ccb_by_idx(p_data->msg.hdr.ccb_idx), |
| p_data->msg.hdr.sig_id, &p_data->msg); |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avdt_scb_set_remove |
| * |
| * Description This function marks an SCB to be removed. |
| * |
| * Returns Nothing. |
| * |
| ******************************************************************************/ |
| void avdt_scb_set_remove(AvdtpScb* p_scb, UNUSED_ATTR tAVDT_SCB_EVT* p_data) { |
| p_scb->remove = true; |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avdt_scb_free_pkt |
| * |
| * Description This function frees the media packet passed in. |
| * |
| * Returns Nothing. |
| * |
| ******************************************************************************/ |
| void avdt_scb_free_pkt(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data) { |
| tAVDT_CTRL avdt_ctrl; |
| |
| /* set error code and parameter */ |
| avdt_ctrl.hdr.err_code = AVDT_ERR_BAD_STATE; |
| avdt_ctrl.hdr.err_param = 0; |
| |
| osi_free_and_reset((void**)&p_data->apiwrite.p_buf); |
| |
| log::warn("Dropped media packet"); |
| |
| /* we need to call callback to keep data flow going */ |
| (*p_scb->stream_config.p_avdt_ctrl_cback)( |
| avdt_scb_to_hdl(p_scb), RawAddress::kEmpty, AVDT_WRITE_CFM_EVT, |
| &avdt_ctrl, p_scb->stream_config.scb_index); |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avdt_scb_clr_pkt |
| * |
| * Description This function frees the media packet stored in the SCB. |
| * |
| * Returns Nothing. |
| * |
| ******************************************************************************/ |
| void avdt_scb_clr_pkt(AvdtpScb* p_scb, UNUSED_ATTR tAVDT_SCB_EVT* p_data) { |
| tAVDT_CTRL avdt_ctrl; |
| AvdtpCcb* p_ccb; |
| uint8_t tcid; |
| uint16_t lcid; |
| |
| /* set error code and parameter */ |
| avdt_ctrl.hdr.err_code = AVDT_ERR_BAD_STATE; |
| avdt_ctrl.hdr.err_param = 0; |
| /* flush the media data queued at L2CAP */ |
| p_ccb = p_scb->p_ccb; |
| if (p_ccb != NULL) { |
| /* get tcid from type, scb */ |
| tcid = avdt_ad_type_to_tcid(AVDT_CHAN_MEDIA, p_scb); |
| |
| lcid = avdtp_cb.ad.rt_tbl[avdt_ccb_to_idx(p_ccb)][tcid].lcid; |
| L2CA_FlushChannel(lcid, L2CAP_FLUSH_CHANS_ALL); |
| } |
| |
| if (p_scb->p_pkt != NULL) { |
| osi_free_and_reset((void**)&p_scb->p_pkt); |
| |
| log::verbose("Dropped stored media packet"); |
| |
| /* we need to call callback to keep data flow going */ |
| (*p_scb->stream_config.p_avdt_ctrl_cback)( |
| avdt_scb_to_hdl(p_scb), RawAddress::kEmpty, AVDT_WRITE_CFM_EVT, |
| &avdt_ctrl, p_scb->stream_config.scb_index); |
| } |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avdt_scb_chk_snd_pkt |
| * |
| * Description This function checks if the SCB is congested, and if not |
| * congested it sends a stored media packet, if any. After it |
| * sends the packet it calls the application callback function |
| * with a write confirm. |
| * |
| * Returns Nothing. |
| * |
| ******************************************************************************/ |
| void avdt_scb_chk_snd_pkt(AvdtpScb* p_scb, UNUSED_ATTR tAVDT_SCB_EVT* p_data) { |
| tAVDT_CTRL avdt_ctrl; |
| BT_HDR* p_pkt; |
| |
| avdt_ctrl.hdr.err_code = 0; |
| |
| if (!p_scb->cong) { |
| if (p_scb->p_pkt != NULL) { |
| p_pkt = p_scb->p_pkt; |
| p_scb->p_pkt = NULL; |
| avdt_ad_write_req(AVDT_CHAN_MEDIA, p_scb->p_ccb, p_scb, p_pkt); |
| |
| (*p_scb->stream_config.p_avdt_ctrl_cback)( |
| avdt_scb_to_hdl(p_scb), RawAddress::kEmpty, AVDT_WRITE_CFM_EVT, |
| &avdt_ctrl, p_scb->stream_config.scb_index); |
| } |
| } |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avdt_scb_transport_channel_timer |
| * |
| * Description This function is called to start a timer when the peer |
| * initiates closing of the stream. The timer verifies that |
| * the peer disconnects the transport channel. |
| * |
| * Returns Nothing. |
| * |
| ******************************************************************************/ |
| void avdt_scb_transport_channel_timer(AvdtpScb* p_scb, |
| UNUSED_ATTR tAVDT_SCB_EVT* p_data) { |
| alarm_set_on_mloop(p_scb->transport_channel_timer, |
| AVDT_SCB_TC_DISC_TIMEOUT_MS, |
| avdt_scb_transport_channel_timer_timeout, p_scb); |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avdt_scb_clr_vars |
| * |
| * Description This function initializes certain SCB variables. |
| * |
| * Returns Nothing. |
| * |
| ******************************************************************************/ |
| void avdt_scb_clr_vars(AvdtpScb* p_scb, UNUSED_ATTR tAVDT_SCB_EVT* p_data) { |
| p_scb->in_use = false; |
| p_scb->peer_seid = 0; |
| } |