| /****************************************************************************** |
| * |
| * Copyright (C) 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. |
| * |
| ******************************************************************************/ |
| |
| #include <string.h> |
| #include "data_types.h" |
| #include "bt_target.h" |
| #include "avdt_api.h" |
| #include "avdtc_api.h" |
| #include "avdt_int.h" |
| #include "gki.h" |
| #include "btu.h" |
| |
| /* 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 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) */ |
| }; |
| |
| /* This table is used to look up the callback event based on the signaling |
| ** role when the stream is closed. |
| */ |
| const UINT8 avdt_scb_role_evt[] = { |
| AVDT_CLOSE_IND_EVT, /* AVDT_CLOSE_ACP */ |
| AVDT_CLOSE_CFM_EVT, /* AVDT_CLOSE_INT */ |
| AVDT_CLOSE_IND_EVT, /* AVDT_OPEN_ACP */ |
| AVDT_OPEN_CFM_EVT /* AVDT_OPEN_INT */ |
| }; |
| |
| /******************************************************************************* |
| ** |
| ** Function avdt_scb_gen_ssrc |
| ** |
| ** Description This function generates a SSRC number unique to the stream. |
| ** |
| ** Returns SSRC value. |
| ** |
| *******************************************************************************/ |
| UINT32 avdt_scb_gen_ssrc(tAVDT_SCB *p_scb) |
| { |
| /* combine the value of the media type and codec type of the SCB */ |
| return ((UINT32)(p_scb->cs.cfg.codec_info[1] | p_scb->cs.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(tAVDT_SCB *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(tAVDT_SCB *p_scb, 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(tAVDT_SCB *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(tAVDT_SCB *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(tAVDT_SCB *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(tAVDT_SCB *p_scb, 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(tAVDT_SCB *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(tAVDT_SCB *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(tAVDT_SCB *p_scb, 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 */ |
| btu_start_timer(&p_scb->timer_entry, BTU_TTYPE_AVDT_SCB_TC, AVDT_SCB_TC_CONN_TOUT); |
| } |
| |
| /******************************************************************************* |
| ** |
| ** Function avdt_scb_hdl_pkt_no_frag |
| ** |
| ** Description |
| ** |
| ** Returns Nothing. |
| ** |
| *******************************************************************************/ |
| void avdt_scb_hdl_pkt_no_frag(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) |
| { |
| UINT8 *p, *p_start; |
| UINT8 o_v, o_p, o_x, o_cc; |
| UINT8 m_pt; |
| UINT8 marker; |
| UINT16 seq; |
| UINT32 time_stamp; |
| UINT32 ssrc; |
| UINT16 offset; |
| UINT16 ex_len; |
| UINT8 pad_len = 0; |
| |
| p = p_start = (UINT8 *)(p_data->p_pkt + 1) + p_data->p_pkt->offset; |
| |
| /* parse media packet header */ |
| 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); |
| BE_STREAM_TO_UINT32(ssrc, p); |
| |
| /* skip over any csrc's in packet */ |
| p += o_cc * 4; |
| |
| /* check for and skip over extension header */ |
| if (o_x) |
| { |
| p += 2; |
| BE_STREAM_TO_UINT16(ex_len, p); |
| p += ex_len * 4; |
| } |
| |
| /* save our new offset */ |
| offset = (UINT16) (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 + p_data->p_pkt->len); |
| } |
| |
| /* do sanity check */ |
| if ((offset > p_data->p_pkt->len) || ((pad_len + offset) > p_data->p_pkt->len)) |
| { |
| AVDT_TRACE_WARNING0("Got bad media packet"); |
| GKI_freebuf(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->cs.p_data_cback != NULL) |
| { |
| /* report sequence number */ |
| p_data->p_pkt->layer_specific = seq; |
| (*p_scb->cs.p_data_cback)(avdt_scb_to_hdl(p_scb), p_data->p_pkt, |
| time_stamp, (UINT8)(m_pt | (marker<<7))); |
| } |
| else |
| { |
| #if AVDT_MULTIPLEXING == TRUE |
| if ((p_scb->cs.p_media_cback != NULL) |
| && (p_scb->p_media_buf != NULL) |
| && (p_scb->media_buf_len > p_data->p_pkt->len)) |
| { |
| /* media buffer enough length is assigned by application. Lets use it*/ |
| memcpy(p_scb->p_media_buf,(UINT8*)(p_data->p_pkt + 1) + p_data->p_pkt->offset, |
| p_data->p_pkt->len); |
| (*p_scb->cs.p_media_cback)(avdt_scb_to_hdl(p_scb),p_scb->p_media_buf, |
| p_scb->media_buf_len,time_stamp,seq,m_pt,marker); |
| } |
| #endif |
| GKI_freebuf(p_data->p_pkt); |
| } |
| } |
| } |
| |
| #if AVDT_REPORTING == TRUE |
| /******************************************************************************* |
| ** |
| ** Function avdt_scb_hdl_report |
| ** |
| ** Description |
| ** |
| ** Returns Nothing. |
| ** |
| *******************************************************************************/ |
| UINT8 * avdt_scb_hdl_report(tAVDT_SCB *p_scb, UINT8 *p, UINT16 len) |
| { |
| UINT16 result = AVDT_SUCCESS; |
| UINT8 *p_start = p; |
| UINT32 ssrc; |
| UINT8 o_v, o_p, o_cc; |
| UINT16 pkt_len; |
| AVDT_REPORT_TYPE pt; |
| tAVDT_REPORT_DATA report, *p_rpt; |
| |
| AVDT_TRACE_DEBUG0( "avdt_scb_hdl_report"); |
| if(p_scb->cs.p_report_cback) |
| { |
| p_rpt = &report; |
| /* parse report packet header */ |
| AVDT_MSG_PRS_RPT_OCTET1(p, o_v, o_p, o_cc); |
| pt = *p++; |
| BE_STREAM_TO_UINT16(pkt_len, p); |
| BE_STREAM_TO_UINT32(ssrc, p); |
| |
| switch(pt) |
| { |
| case AVDT_RTCP_PT_SR: /* the packet type - SR (Sender Report) */ |
| 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) */ |
| 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) */ |
| if(*p == AVDT_RTCP_SDES_CNAME) |
| { |
| p_rpt = (tAVDT_REPORT_DATA *)(p+2); |
| } |
| else |
| { |
| AVDT_TRACE_WARNING5( " - SDES SSRC=0x%08x sc=%d %d len=%d %s", |
| ssrc, o_cc, *p, *(p+1), p+2); |
| result = AVDT_BUSY; |
| } |
| break; |
| |
| default: |
| AVDT_TRACE_ERROR1( "Bad Report pkt - packet type: %d", pt); |
| result = AVDT_BAD_PARAMS; |
| } |
| |
| if(result == AVDT_SUCCESS) |
| (*p_scb->cs.p_report_cback)(avdt_scb_to_hdl(p_scb), pt, p_rpt); |
| |
| } |
| p_start += len; |
| return p_start; |
| } |
| #endif |
| |
| #if AVDT_MULTIPLEXING == TRUE |
| /******************************************************************************* |
| ** |
| ** Function avdt_scb_hdl_pkt_frag |
| ** |
| ** Description |
| ** |
| ** Returns Nothing. |
| ** |
| *******************************************************************************/ |
| void avdt_scb_hdl_pkt_frag(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) |
| { |
| /* Fields of Adaptation Layer Header */ |
| UINT8 al_tsid,al_frag,al_lcode; |
| UINT16 al_len; |
| /* media header fields */ |
| UINT8 o_v, o_p, o_x, o_cc; |
| UINT8 m_pt; |
| UINT8 marker; |
| UINT16 seq; |
| UINT32 time_stamp; |
| UINT32 ssrc; |
| UINT16 ex_len; |
| UINT8 pad_len; |
| /* other variables */ |
| UINT8 *p; /* current pointer */ |
| UINT8 *p_end; /* end of all packet */ |
| UINT8 *p_payload; /* pointer to media fragment payload in the buffer */ |
| UINT32 payload_len; /* payload length */ |
| UINT16 frag_len; /* fragment length */ |
| |
| p = (UINT8 *)(p_data->p_pkt + 1) + p_data->p_pkt->offset; |
| p_end = p + p_data->p_pkt->len; |
| /* parse all fragments */ |
| while(p < p_end) |
| { |
| if (p_end - p < 4) /* length check. maximum length of AL header = 4 */ |
| { |
| AVDT_TRACE_WARNING2("p_end: 0x%x - p:0x%x < 4", p_end, p); |
| break; |
| } |
| |
| /* parse first byte */ |
| al_tsid = (*p)>>3; |
| al_frag = ( (*p) >> 2 ) & 0x01; |
| al_lcode = (*p++) & AVDT_ALH_LCODE_MASK; |
| |
| /* in case of TSID=00000, a second AL header byte, before the length field, |
| ** is expected and contains the actual TSID, aligned with MSB */ |
| if(al_tsid == 0) |
| al_tsid = *p++; |
| |
| /* get remaining media length on base of lcode */ |
| switch(al_lcode) |
| { |
| case AVDT_ALH_LCODE_NONE: /* No length field present. Take length from l2cap */ |
| al_len = (UINT16)(p_end - p); |
| break; |
| case AVDT_ALH_LCODE_16BIT: /* 16 bit length field */ |
| BE_STREAM_TO_UINT16(al_len, p); |
| break; |
| case AVDT_ALH_LCODE_9BITM0: /* 9 bit length field, MSB = 0, 8 LSBs in 1 octet following */ |
| al_len = *p++; |
| break; |
| default: /* 9 bit length field, MSB = 1, 8 LSBs in 1 octet following */ |
| al_len =(UINT16)*p++ + 0x100; |
| } |
| |
| /* max fragment length */ |
| frag_len = (UINT16)(p_end - p); |
| /* if it isn't last fragment */ |
| if(frag_len >= al_len) |
| frag_len = al_len; |
| |
| /* check TSID corresponds to config */ |
| if (al_tsid != p_scb->curr_cfg.mux_tsid_media) |
| { |
| #if AVDT_REPORTING == TRUE |
| if((p_scb->curr_cfg.psc_mask & AVDT_PSC_REPORT) && |
| (al_tsid == p_scb->curr_cfg.mux_tsid_report)) |
| { |
| /* parse reporting packet */ |
| p = avdt_scb_hdl_report(p_scb, p, frag_len); |
| continue; |
| } |
| else |
| #endif |
| { |
| AVDT_TRACE_WARNING2("bad tsid: %d, mux_tsid_media:%d", al_tsid, p_scb->curr_cfg.mux_tsid_media); |
| break; |
| } |
| } |
| /* check are buffer for assembling and related callback set */ |
| else if ((p_scb->p_media_buf == NULL) || (p_scb->cs.p_media_cback == NULL)) |
| { |
| AVDT_TRACE_WARNING0("NULL p_media_buf or p_media_cback"); |
| break; |
| } |
| |
| |
| /* it is media fragment beginning */ |
| if(!al_frag) /* is it first fragment of original media packet */ |
| { |
| AVDT_TRACE_DEBUG2("al:%d media:%d", |
| al_len, p_scb->media_buf_len); |
| |
| p_scb->frag_off = 0; |
| p_scb->frag_org_len = al_len; /* total length of original media packet */ |
| /* length check: minimum length of media header is 12 */ |
| if (p_scb->frag_org_len < 12) |
| { |
| AVDT_TRACE_WARNING1("bad al_len: %d(<12)", al_len); |
| break; |
| } |
| /* check that data fit into buffer */ |
| if (al_len > p_scb->media_buf_len) |
| { |
| AVDT_TRACE_WARNING2("bad al_len: %d(>%d)", al_len, p_scb->media_buf_len); |
| break; |
| } |
| /* make sure it is the last fragment in l2cap packet */ |
| if (p + al_len < p_end) |
| { |
| AVDT_TRACE_WARNING2("bad al_len: %d(>%d)", al_len, p_scb->media_buf_len); |
| break; |
| } |
| } |
| else |
| { |
| AVDT_TRACE_DEBUG4("al:%d media:%d frag_org_len:%d frag_off:%d", |
| al_len, p_scb->media_buf_len, p_scb->frag_org_len, p_scb->frag_off); |
| |
| /* check that remaining length from AL header equals to original len - length of already received fragments */ |
| if(al_len != p_scb->frag_org_len - p_scb->frag_off) |
| { |
| AVDT_TRACE_WARNING4("al_len:%d != (frag_org_len:%d - frag_off:%d) %d", |
| al_len, p_scb->frag_org_len, p_scb->frag_off, |
| (p_scb->frag_org_len- p_scb->frag_off)); |
| break; |
| } |
| |
| /* do sanity check */ |
| if (p_scb->frag_off == 0) |
| { |
| AVDT_TRACE_WARNING0("frag_off=0"); |
| break; |
| } |
| } |
| /* do common sanity check */ |
| if((p_scb->frag_org_len <= p_scb->frag_off) || (p_scb->frag_org_len >= p_scb->media_buf_len)) |
| { |
| AVDT_TRACE_WARNING3("common sanity frag_off:%d frag_org_len:%d media_buf_len:%d", |
| p_scb->frag_off, p_scb->frag_org_len, p_scb->media_buf_len); |
| break; |
| } |
| |
| AVDT_TRACE_DEBUG4("Received fragment org_len=%d off=%d al_len=%d frag_len=%d", |
| p_scb->frag_org_len, p_scb->frag_off, al_len, frag_len); |
| |
| /* copy fragment into buffer */ |
| memcpy(p_scb->p_media_buf + p_scb->frag_off, p, frag_len); |
| p_scb->frag_off += frag_len; |
| /* move to the next fragment */ |
| p += frag_len; |
| /* if it is last fragment in original media packet then process total media pocket */ |
| if(p_scb->frag_off == p_scb->frag_org_len) |
| { |
| p_payload = p_scb->p_media_buf; |
| |
| /* media header */ |
| AVDT_MSG_PRS_OCTET1(p_payload, o_v, o_p, o_x, o_cc); |
| AVDT_MSG_PRS_M_PT(p_payload, m_pt, marker); |
| BE_STREAM_TO_UINT16(seq, p_payload); |
| BE_STREAM_TO_UINT32(time_stamp, p_payload); |
| BE_STREAM_TO_UINT32(ssrc, p_payload); |
| |
| /* skip over any csrc's in packet */ |
| p_payload += o_cc * 4; |
| |
| /* check for and skip over extension header */ |
| if (o_x) |
| { |
| if(p_scb->p_media_buf + p_scb->frag_off - p_payload < 4) |
| { |
| AVDT_TRACE_WARNING3("length check frag_off:%d p_media_buf:%d p_payload:%d", |
| p_scb->frag_off, p_scb->p_media_buf, p_payload); |
| break;/* length check */ |
| } |
| p_payload += 2; |
| BE_STREAM_TO_UINT16(ex_len, p_payload); |
| p_payload += ex_len * 4; |
| } |
| |
| if(p_payload >= p_scb->p_media_buf + p_scb->frag_off) |
| { |
| AVDT_TRACE_WARNING3("length check2 frag_off:%d p_media_buf:%d p_payload:%d", |
| p_scb->frag_off, p_scb->p_media_buf, p_payload); |
| break;/* length check */ |
| } |
| |
| /* adjust length for any padding at end of packet */ |
| if (o_p) |
| { |
| /* padding length in last byte of packet */ |
| pad_len = *(p_scb->p_media_buf + p_scb->frag_off - 1); |
| } |
| else |
| pad_len = 0; |
| /* payload length */ |
| payload_len = (UINT32)(p_scb->p_media_buf + p_scb->frag_off - pad_len - p_payload); |
| |
| AVDT_TRACE_DEBUG2("Received last fragment header=%d len=%d", |
| p_payload - p_scb->p_media_buf,payload_len); |
| |
| /* send total media packet up */ |
| if (p_scb->cs.p_media_cback != NULL) |
| { |
| (*p_scb->cs.p_media_cback)(avdt_scb_to_hdl(p_scb), p_payload, |
| payload_len, time_stamp, seq, m_pt, marker); |
| } |
| } |
| } /* while(p < p_end) */ |
| |
| if(p < p_end) |
| { |
| AVDT_TRACE_WARNING0("*** Got bad media packet"); |
| } |
| GKI_freebuf(p_data->p_pkt); |
| } |
| #endif |
| |
| /******************************************************************************* |
| ** |
| ** Function avdt_scb_hdl_pkt |
| ** |
| ** Description |
| ** |
| ** Returns Nothing. |
| ** |
| *******************************************************************************/ |
| void avdt_scb_hdl_pkt(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) |
| { |
| #if AVDT_REPORTING == TRUE |
| UINT8 *p; |
| #endif |
| |
| #if AVDT_MULTIPLEXING == TRUE |
| /* select right function in dependance of is fragmentation supported or not */ |
| if( 0 != (p_scb->curr_cfg.psc_mask & AVDT_PSC_MUX)) |
| { |
| avdt_scb_hdl_pkt_frag(p_scb, p_data); |
| } |
| else |
| #endif |
| #if AVDT_REPORTING == TRUE |
| if(p_data->p_pkt->layer_specific == AVDT_CHAN_REPORT) |
| { |
| p = (UINT8 *)(p_data->p_pkt + 1) + p_data->p_pkt->offset; |
| avdt_scb_hdl_report(p_scb, p, p_data->p_pkt->len); |
| GKI_freebuf(p_data->p_pkt); |
| } |
| else |
| #endif |
| 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(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) |
| { |
| GKI_freebuf(p_data->p_pkt); |
| AVDT_TRACE_WARNING0("Dropped incoming media packet"); |
| } |
| |
| /******************************************************************************* |
| ** |
| ** 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(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) |
| { |
| /* if command not supported */ |
| if (p_scb->cs.nsc_mask & 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 */ |
| memcpy(&p_scb->req_cfg, p_data->msg.reconfig_cmd.p_cfg, sizeof(tAVDT_CFG)); |
| |
| /* call application callback */ |
| (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb), |
| NULL, |
| AVDT_RECONFIG_IND_EVT, |
| (tAVDT_CTRL *) &p_data->msg.reconfig_cmd); |
| } |
| } |
| |
| /******************************************************************************* |
| ** |
| ** 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(tAVDT_SCB *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->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb), |
| NULL, |
| AVDT_RECONFIG_CFM_EVT, |
| (tAVDT_CTRL *) &p_data->msg.svccap); |
| } |
| |
| /******************************************************************************* |
| ** |
| ** 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(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) |
| { |
| /* if command not supported */ |
| if (p_scb->cs.nsc_mask & 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->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb), |
| NULL, |
| AVDT_SECURITY_IND_EVT, |
| (tAVDT_CTRL *) &p_data->msg.security_cmd); |
| } |
| } |
| |
| /******************************************************************************* |
| ** |
| ** 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(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) |
| { |
| /* call application callback */ |
| (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb), |
| NULL, |
| AVDT_SECURITY_CFM_EVT, |
| (tAVDT_CTRL *) &p_data->msg.security_cmd); |
| } |
| |
| /******************************************************************************* |
| ** |
| ** 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(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) |
| { |
| tAVDT_CFG *p_cfg; |
| |
| if (!p_scb->in_use) |
| { |
| p_cfg = p_data->msg.config_cmd.p_cfg; |
| if(p_scb->cs.cfg.codec_info[AVDT_CODEC_TYPE_INDEX] == p_cfg->codec_info[AVDT_CODEC_TYPE_INDEX]) |
| { |
| /* set sep as in use */ |
| p_scb->in_use = TRUE; |
| |
| /* copy info to scb */ |
| p_scb->p_ccb = avdt_ccb_by_idx(p_data->msg.config_cmd.hdr.ccb_idx); |
| p_scb->peer_seid = p_data->msg.config_cmd.int_seid; |
| memcpy(&p_scb->req_cfg, p_cfg, sizeof(tAVDT_CFG)); |
| /* call app callback */ |
| (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb), |
| p_scb->p_ccb ? p_scb->p_ccb->peer_addr : NULL, |
| AVDT_CONFIG_IND_EVT, |
| (tAVDT_CTRL *) &p_data->msg.config_cmd); |
| } |
| 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 |
| { |
| 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(tAVDT_SCB *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->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb), |
| NULL, |
| AVDT_OPEN_CFM_EVT, |
| (tAVDT_CTRL *) &p_data->msg.hdr); |
| } |
| |
| /******************************************************************************* |
| ** |
| ** 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. |
| ** |
| ** Returns Nothing. |
| ** |
| *******************************************************************************/ |
| void avdt_scb_hdl_setconfig_rsp(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) |
| { |
| tAVDT_EVT_HDR single; |
| |
| if (p_scb->p_ccb != NULL) |
| { |
| /* save configuration */ |
| memcpy(&p_scb->curr_cfg, &p_scb->req_cfg, sizeof(tAVDT_CFG)); |
| |
| /* initiate open */ |
| single.seid = p_scb->peer_seid; |
| avdt_scb_event(p_scb, AVDT_SCB_API_OPEN_REQ_EVT, (tAVDT_SCB_EVT *) &single); |
| } |
| } |
| |
| /******************************************************************************* |
| ** |
| ** 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(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) |
| { |
| (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb), |
| p_scb->p_ccb ? p_scb->p_ccb->peer_addr : NULL, |
| AVDT_START_IND_EVT, |
| NULL); |
| } |
| |
| /******************************************************************************* |
| ** |
| ** 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(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) |
| { |
| (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb), |
| p_scb->p_ccb ? p_scb->p_ccb->peer_addr : NULL, |
| AVDT_START_CFM_EVT, |
| (tAVDT_CTRL *) &p_data->msg.hdr); |
| } |
| |
| /******************************************************************************* |
| ** |
| ** 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(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) |
| { |
| (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb), |
| p_scb->p_ccb ? p_scb->p_ccb->peer_addr : NULL, |
| AVDT_SUSPEND_IND_EVT, |
| NULL); |
| } |
| |
| /******************************************************************************* |
| ** |
| ** 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(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) |
| { |
| (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb), |
| p_scb->p_ccb ? p_scb->p_ccb->peer_addr : NULL, |
| AVDT_SUSPEND_CFM_EVT, |
| (tAVDT_CTRL *) &p_data->msg.hdr); |
| } |
| |
| /******************************************************************************* |
| ** |
| ** 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(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) |
| { |
| UINT8 hdl = avdt_scb_to_hdl(p_scb); |
| tAVDT_CTRL_CBACK *p_ctrl_cback = p_scb->cs.p_ctrl_cback; |
| tAVDT_CTRL avdt_ctrl; |
| UINT8 event; |
| tAVDT_CCB *p_ccb = p_scb->p_ccb; |
| BD_ADDR remote_addr; |
| |
| |
| memcpy (remote_addr, p_ccb->peer_addr, BD_ADDR_LEN); |
| |
| /* 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 */ |
| if (p_scb->p_pkt != NULL) |
| { |
| GKI_freebuf(p_scb->p_pkt); |
| p_scb->p_pkt = NULL; |
| } |
| |
| /* stop transport channel timer */ |
| btu_stop_timer(&p_scb->timer_entry); |
| |
| 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_ctrl_cback)(hdl, remote_addr, event, &avdt_ctrl); |
| } |
| |
| /******************************************************************************* |
| ** |
| ** 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 (tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) |
| { |
| 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 (tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) |
| { |
| (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb), |
| p_scb->p_ccb ? p_scb->p_ccb->peer_addr : NULL, |
| AVDT_DELAY_REPORT_EVT, |
| (tAVDT_CTRL *) &p_data->msg.hdr); |
| |
| 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 (tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) |
| { |
| (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb), |
| p_scb->p_ccb ? p_scb->p_ccb->peer_addr : NULL, |
| AVDT_DELAY_REPORT_CFM_EVT, |
| (tAVDT_CTRL *) &p_data->msg.hdr); |
| } |
| |
| #if AVDT_REPORTING == TRUE |
| /******************************************************************************* |
| ** |
| ** 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(tAVDT_SCB *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->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb), |
| p_scb->p_ccb ? p_scb->p_ccb->peer_addr : NULL, |
| AVDT_REPORT_DISCONN_EVT, &avdt_ctrl); |
| } |
| } |
| 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); |
| } |
| } |
| #endif |
| |
| /******************************************************************************* |
| ** |
| ** 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(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) |
| { |
| UINT8 event; |
| #if AVDT_REPORTING == TRUE |
| UINT8 role; |
| #endif |
| |
| /* stop transport channel connect timer */ |
| btu_stop_timer(&p_scb->timer_entry); |
| |
| event = (p_scb->role == AVDT_OPEN_INT) ? AVDT_OPEN_CFM_EVT : AVDT_OPEN_IND_EVT; |
| p_data->open.hdr.err_code = 0; |
| |
| AVDT_TRACE_DEBUG3("psc_mask: cfg: 0x%x, req:0x%x, cur: 0x%x", |
| p_scb->cs.cfg.psc_mask, p_scb->req_cfg.psc_mask, p_scb->curr_cfg.psc_mask); |
| #if AVDT_REPORTING == TRUE |
| 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); |
| } |
| #endif |
| |
| /* call app callback */ |
| (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb), |
| p_scb->p_ccb ? p_scb->p_ccb->peer_addr : NULL, |
| event, |
| (tAVDT_CTRL *) &p_data->open); |
| } |
| |
| #if AVDT_REPORTING == TRUE |
| /******************************************************************************* |
| ** |
| ** 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(tAVDT_SCB *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->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb), |
| p_scb->p_ccb ? p_scb->p_ccb->peer_addr : NULL, |
| AVDT_REPORT_CONN_EVT, &avdt_ctrl); |
| } |
| } |
| #endif |
| |
| /******************************************************************************* |
| ** |
| ** Function avdt_scb_hdl_write_req_no_frag |
| ** |
| ** 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_no_frag(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) |
| { |
| UINT8 *p; |
| UINT32 ssrc; |
| |
| /* free packet we're holding, if any; to be replaced with new */ |
| if (p_scb->p_pkt != NULL) |
| { |
| GKI_freebuf(p_scb->p_pkt); |
| |
| /* this shouldn't be happening */ |
| AVDT_TRACE_WARNING0("Dropped media packet; congested"); |
| } |
| |
| /* build a media packet */ |
| /* Add RTP header if required */ |
| if ( !(p_data->apiwrite.opt & AVDT_DATA_OPT_NO_RTP) ) |
| { |
| 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 = (UINT8 *)(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); |
| } |
| |
| p_scb->media_seq++; |
| |
| /* store it */ |
| p_scb->p_pkt = p_data->apiwrite.p_buf; |
| } |
| |
| #if AVDT_MULTIPLEXING == TRUE |
| /******************************************************************************* |
| ** |
| ** Function avdt_scb_hdl_write_req_frag |
| ** |
| ** Description This function builds a new fragments of media packet from |
| ** the passed in buffers and stores them in the SCB. |
| ** |
| ** Returns Nothing. |
| ** |
| *******************************************************************************/ |
| void avdt_scb_hdl_write_req_frag(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) |
| { |
| UINT8 *p; |
| UINT32 ssrc; |
| BT_HDR *p_frag; |
| |
| /* free fragments we're holding, if any; it shouldn't happen */ |
| if (!GKI_queue_is_empty(&p_scb->frag_q)) |
| { |
| while((p_frag = (BT_HDR*)GKI_dequeue (&p_scb->frag_q)) != NULL) |
| GKI_freebuf(p_frag); |
| |
| /* this shouldn't be happening */ |
| AVDT_TRACE_WARNING0("*** Dropped media packet; congested"); |
| } |
| |
| /* build a media fragments */ |
| p_scb->frag_off = p_data->apiwrite.data_len; |
| p_scb->p_next_frag = p_data->apiwrite.p_data; |
| |
| ssrc = avdt_scb_gen_ssrc(p_scb); |
| |
| /* get first packet */ |
| p_frag = (BT_HDR*)GKI_getfirst (&p_data->apiwrite.frag_q); |
| /* posit on Adaptation Layer header */ |
| p_frag->len += AVDT_AL_HDR_SIZE + AVDT_MEDIA_HDR_SIZE; |
| p_frag->offset -= AVDT_AL_HDR_SIZE + AVDT_MEDIA_HDR_SIZE; |
| p = (UINT8 *)(p_frag + 1) + p_frag->offset; |
| |
| /* Adaptation Layer header */ |
| /* TSID, no-fragment bit and coding of length(in 2 length octets following) */ |
| *p++ = (p_scb->curr_cfg.mux_tsid_media<<3) | AVDT_ALH_LCODE_16BIT; |
| |
| /* length of all remaining transport packet */ |
| UINT16_TO_BE_STREAM(p, p_frag->layer_specific+AVDT_MEDIA_HDR_SIZE ); |
| /* media header */ |
| 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); |
| p_scb->media_seq++; |
| |
| while((p_frag = (BT_HDR*)GKI_getnext (p_frag)) != NULL) |
| { |
| /* posit on Adaptation Layer header */ |
| p_frag->len += AVDT_AL_HDR_SIZE; |
| p_frag->offset -= AVDT_AL_HDR_SIZE; |
| p = (UINT8 *)(p_frag + 1) + p_frag->offset; |
| /* Adaptation Layer header */ |
| /* TSID, fragment bit and coding of length(in 2 length octets following) */ |
| *p++ = (p_scb->curr_cfg.mux_tsid_media<<3) | (AVDT_ALH_FRAG_MASK|AVDT_ALH_LCODE_16BIT); |
| |
| /* length of all remaining transport packet */ |
| UINT16_TO_BE_STREAM(p, p_frag->layer_specific ); |
| } |
| |
| /* store it */ |
| p_scb->frag_q = p_data->apiwrite.frag_q; |
| } |
| #endif |
| |
| |
| /******************************************************************************* |
| ** |
| ** Function avdt_scb_hdl_write_req |
| ** |
| ** Description This function calls one of the two versions of building functions |
| ** for case with and without fragmentation |
| ** |
| ** Returns Nothing. |
| ** |
| *******************************************************************************/ |
| void avdt_scb_hdl_write_req(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) |
| { |
| #if AVDT_MULTIPLEXING == TRUE |
| if (GKI_queue_is_empty(&p_data->apiwrite.frag_q)) |
| #endif |
| avdt_scb_hdl_write_req_no_frag(p_scb, p_data); |
| #if AVDT_MULTIPLEXING == TRUE |
| else |
| avdt_scb_hdl_write_req_frag(p_scb, p_data); |
| #endif |
| } |
| |
| /******************************************************************************* |
| ** |
| ** Function avdt_scb_snd_abort_req |
| ** |
| ** Description This function sends an abort command message. |
| ** |
| ** Returns Nothing. |
| ** |
| *******************************************************************************/ |
| void avdt_scb_snd_abort_req(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) |
| { |
| tAVDT_EVT_HDR hdr; |
| |
| if (p_scb->p_ccb != NULL) |
| { |
| p_scb->role = AVDT_CLOSE_INT; |
| |
| hdr.seid = p_scb->peer_seid; |
| |
| avdt_msg_send_cmd(p_scb->p_ccb, p_scb, AVDT_SIG_ABORT, (tAVDT_MSG *) &hdr); |
| } |
| } |
| |
| /******************************************************************************* |
| ** |
| ** Function avdt_scb_snd_abort_rsp |
| ** |
| ** Description This function sends an abort response message. |
| ** |
| ** Returns Nothing. |
| ** |
| *******************************************************************************/ |
| void avdt_scb_snd_abort_rsp(tAVDT_SCB *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(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) |
| { |
| tAVDT_EVT_HDR hdr; |
| |
| p_scb->role = AVDT_CLOSE_INT; |
| |
| hdr.seid = p_scb->peer_seid; |
| |
| avdt_msg_send_cmd(p_scb->p_ccb, p_scb, AVDT_SIG_CLOSE, (tAVDT_MSG *) &hdr); |
| } |
| |
| /******************************************************************************* |
| ** |
| ** Function avdt_scb_snd_stream_close |
| ** |
| ** Description This function sends a close command message. |
| ** |
| ** Returns Nothing. |
| ** |
| *******************************************************************************/ |
| void avdt_scb_snd_stream_close(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) |
| { |
| #if AVDT_MULTIPLEXING == TRUE |
| BT_HDR *p_frag; |
| |
| AVDT_TRACE_WARNING2("avdt_scb_snd_stream_close c:%d, off:%d", |
| p_scb->frag_q.count, p_scb->frag_off); |
| /* clean fragments queue */ |
| while((p_frag = (BT_HDR*)GKI_dequeue (&p_scb->frag_q)) != NULL) |
| GKI_freebuf(p_frag); |
| p_scb->frag_off = 0; |
| #endif |
| if (p_scb->p_pkt) |
| { |
| GKI_freebuf(p_scb->p_pkt); |
| p_scb->p_pkt = NULL; |
| } |
| |
| #if 0 |
| if(p_scb->cong) |
| p_scb->cong = FALSE; |
| |
| /* p_scb->curr_cfg.mux_tsid_media == 0 */ |
| #endif |
| 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(tAVDT_SCB *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(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) |
| { |
| tAVDT_EVT_HDR hdr; |
| |
| hdr.seid = p_scb->peer_seid; |
| |
| avdt_msg_send_cmd(p_scb->p_ccb, p_scb, AVDT_SIG_GETCONFIG, (tAVDT_MSG *) &hdr); |
| } |
| |
| /******************************************************************************* |
| ** |
| ** Function avdt_scb_snd_getconfig_rsp |
| ** |
| ** Description This function sends a get configuration response message. |
| ** |
| ** Returns Nothing. |
| ** |
| *******************************************************************************/ |
| void avdt_scb_snd_getconfig_rsp(tAVDT_SCB *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(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) |
| { |
| tAVDT_EVT_HDR hdr; |
| |
| hdr.seid = p_scb->peer_seid; |
| |
| avdt_msg_send_cmd(p_scb->p_ccb, p_scb, AVDT_SIG_OPEN, (tAVDT_MSG *) &hdr); |
| } |
| |
| /******************************************************************************* |
| ** |
| ** 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(tAVDT_SCB *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); |
| |
| /* start tc connect timer */ |
| btu_start_timer(&p_scb->timer_entry, BTU_TTYPE_AVDT_SCB_TC, AVDT_SCB_TC_CONN_TOUT); |
| } |
| |
| /******************************************************************************* |
| ** |
| ** 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(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) |
| { |
| memcpy(&p_scb->req_cfg, p_data->msg.config_cmd.p_cfg, sizeof(tAVDT_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(tAVDT_SCB *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(tAVDT_SCB *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(tAVDT_SCB *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(tAVDT_SCB *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(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) |
| { |
| tAVDT_CFG *p_req, *p_cfg; |
| |
| /* copy API parameters to scb, set scb as in use */ |
| p_scb->in_use = TRUE; |
| p_scb->p_ccb = avdt_ccb_by_idx(p_data->msg.config_cmd.hdr.ccb_idx); |
| p_scb->peer_seid = p_data->msg.config_cmd.hdr.seid; |
| p_req = p_data->msg.config_cmd.p_cfg; |
| p_cfg = &p_scb->cs.cfg; |
| #if AVDT_MULTIPLEXING == TRUE |
| p_req->mux_tsid_media = p_cfg->mux_tsid_media; |
| p_req->mux_tcid_media = p_cfg->mux_tcid_media; |
| if(p_req->psc_mask & AVDT_PSC_REPORT) |
| { |
| p_req->mux_tsid_report = p_cfg->mux_tsid_report; |
| p_req->mux_tcid_report = p_cfg->mux_tcid_report; |
| } |
| #endif |
| memcpy(&p_scb->req_cfg, p_data->msg.config_cmd.p_cfg, sizeof(tAVDT_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(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) |
| { |
| if (p_scb->p_ccb != NULL) |
| { |
| memcpy(&p_scb->curr_cfg, &p_scb->req_cfg, sizeof(tAVDT_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(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) |
| { |
| #if AVDT_REPORTING == TRUE |
| if(p_scb->curr_cfg.psc_mask & AVDT_PSC_REPORT) |
| avdt_ad_close_req(AVDT_CHAN_REPORT, p_scb->p_ccb, p_scb); |
| #endif |
| 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(tAVDT_SCB *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; |
| |
| /* call callback, using lookup table to get callback event */ |
| (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb), |
| NULL, |
| avdt_scb_cback_evt[p_scb->curr_evt], |
| &avdt_ctrl); |
| } |
| |
| /******************************************************************************* |
| ** |
| ** 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(tAVDT_SCB *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(tAVDT_SCB *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(tAVDT_SCB *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(tAVDT_SCB *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(tAVDT_SCB *p_scb, 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(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) |
| { |
| tAVDT_CTRL avdt_ctrl; |
| #if AVDT_MULTIPLEXING == TRUE |
| BT_HDR *p_frag; |
| #endif |
| |
| /* set error code and parameter */ |
| avdt_ctrl.hdr.err_code = AVDT_ERR_BAD_STATE; |
| avdt_ctrl.hdr.err_param = 0; |
| |
| /* p_buf can be NULL in case using of fragments queue frag_q */ |
| if(p_data->apiwrite.p_buf) |
| GKI_freebuf(p_data->apiwrite.p_buf); |
| |
| #if AVDT_MULTIPLEXING == TRUE |
| /* clean fragments queue */ |
| while((p_frag = (BT_HDR*)GKI_dequeue (&p_data->apiwrite.frag_q)) != NULL) |
| GKI_freebuf(p_frag); |
| #endif |
| |
| AVDT_TRACE_WARNING0("Dropped media packet"); |
| |
| /* we need to call callback to keep data flow going */ |
| (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb), NULL, AVDT_WRITE_CFM_EVT, |
| &avdt_ctrl); |
| } |
| |
| /******************************************************************************* |
| ** |
| ** Function avdt_scb_clr_pkt |
| ** |
| ** Description This function frees the media packet stored in the SCB. |
| ** |
| ** Returns Nothing. |
| ** |
| *******************************************************************************/ |
| void avdt_scb_clr_pkt(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) |
| { |
| tAVDT_CTRL avdt_ctrl; |
| tAVDT_CCB *p_ccb; |
| UINT8 tcid; |
| UINT16 lcid; |
| #if AVDT_MULTIPLEXING == TRUE |
| BT_HDR *p_frag; |
| #endif |
| |
| /* 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 */ |
| if((p_ccb = p_scb->p_ccb) != NULL) |
| { |
| /* get tcid from type, scb */ |
| tcid = avdt_ad_type_to_tcid(AVDT_CHAN_MEDIA, p_scb); |
| |
| lcid = avdt_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) |
| { |
| GKI_freebuf(p_scb->p_pkt); |
| p_scb->p_pkt = NULL; |
| |
| AVDT_TRACE_DEBUG0("Dropped stored media packet"); |
| |
| /* we need to call callback to keep data flow going */ |
| (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb), NULL, AVDT_WRITE_CFM_EVT, |
| &avdt_ctrl); |
| } |
| #if AVDT_MULTIPLEXING == TRUE |
| else if(!GKI_queue_is_empty (&p_scb->frag_q)) |
| { |
| AVDT_TRACE_DEBUG0("Dropped fragments queue"); |
| /* clean fragments queue */ |
| while((p_frag = (BT_HDR*)GKI_dequeue (&p_scb->frag_q)) != NULL) |
| GKI_freebuf(p_frag); |
| |
| p_scb->frag_off = 0; |
| |
| /* we need to call callback to keep data flow going */ |
| (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb), NULL, AVDT_WRITE_CFM_EVT, |
| &avdt_ctrl); |
| } |
| #endif |
| } |
| |
| |
| /******************************************************************************* |
| ** |
| ** 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(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) |
| { |
| tAVDT_CTRL avdt_ctrl; |
| BT_HDR *p_pkt; |
| #if AVDT_MULTIPLEXING == TRUE |
| BOOLEAN sent = FALSE; |
| UINT8 res = AVDT_AD_SUCCESS; |
| tAVDT_SCB_EVT data; |
| #endif |
| |
| 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->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb), NULL, AVDT_WRITE_CFM_EVT, &avdt_ctrl); |
| } |
| #if AVDT_MULTIPLEXING == TRUE |
| else |
| { |
| #if 0 |
| AVDT_TRACE_DEBUG1("num_q=%d", |
| L2CA_FlushChannel(avdt_cb.ad.rt_tbl[avdt_ccb_to_idx(p_scb->p_ccb)][avdt_ad_type_to_tcid(AVDT_CHAN_MEDIA, p_scb)].lcid), |
| L2CAP_FLUSH_CHANS_GET); |
| #endif |
| while((p_pkt = (BT_HDR*)GKI_dequeue (&p_scb->frag_q)) != NULL) |
| { |
| sent = TRUE; |
| AVDT_TRACE_DEBUG1("Send fragment len=%d",p_pkt->len); |
| /* fragments queue contains fragment to send */ |
| res = avdt_ad_write_req(AVDT_CHAN_MEDIA, p_scb->p_ccb, p_scb, p_pkt); |
| if(AVDT_AD_CONGESTED == res) |
| { |
| p_scb->cong = TRUE; |
| AVDT_TRACE_DEBUG0("avdt/l2c congested!!"); |
| break;/* exit loop if channel became congested */ |
| } |
| } |
| AVDT_TRACE_DEBUG2("res=%d left=%d",res, p_scb->frag_off); |
| |
| if(p_scb->frag_off) |
| { |
| if(AVDT_AD_SUCCESS == res || GKI_queue_is_empty (&p_scb->frag_q)) |
| { |
| /* all buffers were sent to L2CAP, compose more to queue */ |
| avdt_scb_queue_frags(p_scb, &p_scb->p_next_frag, &p_scb->frag_off, &p_scb->frag_q); |
| if(!GKI_queue_is_empty (&p_scb->frag_q)) |
| { |
| data.llcong = p_scb->cong; |
| avdt_scb_event(p_scb, AVDT_SCB_TC_CONG_EVT, &data); |
| } |
| } |
| } |
| |
| /* Send event AVDT_WRITE_CFM_EVT if it was last fragment */ |
| else if (sent && GKI_queue_is_empty (&p_scb->frag_q)) |
| { |
| (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb), NULL, AVDT_WRITE_CFM_EVT, &avdt_ctrl); |
| } |
| } |
| #endif |
| } |
| } |
| |
| /******************************************************************************* |
| ** |
| ** Function avdt_scb_tc_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_tc_timer(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) |
| { |
| btu_start_timer(&p_scb->timer_entry, BTU_TTYPE_AVDT_SCB_TC, AVDT_SCB_TC_DISC_TOUT); |
| } |
| |
| /******************************************************************************* |
| ** |
| ** Function avdt_scb_clr_vars |
| ** |
| ** Description This function initializes certain SCB variables. |
| ** |
| ** Returns Nothing. |
| ** |
| *******************************************************************************/ |
| void avdt_scb_clr_vars(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) |
| { |
| p_scb->in_use = FALSE; |
| p_scb->p_ccb = NULL; |
| p_scb->peer_seid = 0; |
| } |
| |
| #if AVDT_MULTIPLEXING == TRUE |
| /******************************************************************************* |
| ** |
| ** Function avdt_scb_queue_frags |
| ** |
| ** Description This function breaks media payload into fragments |
| ** and put the fragments in the given queue. |
| ** |
| ** Returns Nothing. |
| ** |
| *******************************************************************************/ |
| void avdt_scb_queue_frags(tAVDT_SCB *p_scb, UINT8 **pp_data, UINT32 *p_data_len, BUFFER_Q *pq) |
| { |
| UINT16 lcid; |
| UINT16 num_frag; |
| UINT16 mtu_used; |
| UINT8 *p; |
| BOOLEAN al_hdr = FALSE; |
| UINT8 tcid; |
| tAVDT_TC_TBL *p_tbl; |
| UINT16 buf_size; |
| UINT16 offset = AVDT_MEDIA_OFFSET + AVDT_AL_HDR_SIZE; |
| UINT16 cont_offset = offset - AVDT_MEDIA_HDR_SIZE; |
| BT_HDR *p_frag; |
| |
| tcid = avdt_ad_type_to_tcid(AVDT_CHAN_MEDIA, p_scb); |
| lcid = avdt_cb.ad.rt_tbl[avdt_ccb_to_idx(p_scb->p_ccb)][tcid].lcid; |
| |
| if( p_scb->frag_off != 0) |
| { |
| /* continuing process is usually triggered by un-congest event. |
| * the number of buffers at L2CAP is very small (if not 0). |
| * we do not need to L2CA_FlushChannel() */ |
| offset = cont_offset; |
| al_hdr = TRUE; |
| num_frag = AVDT_MAX_FRAG_COUNT; |
| } |
| else |
| { |
| num_frag = L2CA_FlushChannel(lcid, L2CAP_FLUSH_CHANS_GET); |
| AVDT_TRACE_DEBUG2("num_q=%d lcid=%d", num_frag, lcid); |
| if(num_frag >= AVDT_MAX_FRAG_COUNT) |
| { |
| num_frag = 0; |
| } |
| else |
| { |
| num_frag = AVDT_MAX_FRAG_COUNT - num_frag; |
| } |
| } |
| |
| /* look up transport channel table entry to get peer mtu */ |
| p_tbl = avdt_ad_tc_tbl_by_type(AVDT_CHAN_MEDIA, p_scb->p_ccb, p_scb); |
| buf_size = p_tbl->peer_mtu + BT_HDR_SIZE; |
| AVDT_TRACE_DEBUG3("peer_mtu: %d, buf_size: %d num_frag=%d", |
| p_tbl->peer_mtu, buf_size, num_frag); |
| |
| if(buf_size > AVDT_DATA_POOL_SIZE) |
| buf_size = AVDT_DATA_POOL_SIZE; |
| |
| mtu_used = buf_size - BT_HDR_SIZE; |
| |
| while(*p_data_len && num_frag) |
| { |
| /* allocate buffer for fragment */ |
| if(NULL == (p_frag = (BT_HDR*)GKI_getbuf(buf_size))) |
| { |
| AVDT_TRACE_WARNING1("avdt_scb_queue_frags len=%d(out of GKI buffers)",*p_data_len); |
| break; |
| } |
| /* fill fragment by chunk of media payload */ |
| p_frag->layer_specific = *p_data_len;/* length of all remaining transport packet */ |
| p_frag->offset = offset; |
| /* adjust packet offset for continuing packets */ |
| offset = cont_offset; |
| |
| p_frag->len = mtu_used - p_frag->offset; |
| if(p_frag->len > *p_data_len) |
| p_frag->len = *p_data_len; |
| memcpy((UINT8*)(p_frag+1) + p_frag->offset, *pp_data, p_frag->len); |
| *pp_data += p_frag->len; |
| *p_data_len -= p_frag->len; |
| AVDT_TRACE_DEBUG1("Prepared fragment len=%d", p_frag->len); |
| |
| if(al_hdr) |
| { |
| /* Adaptation Layer header */ |
| p_frag->len += AVDT_AL_HDR_SIZE; |
| p_frag->offset -= AVDT_AL_HDR_SIZE; |
| p = (UINT8 *)(p_frag + 1) + p_frag->offset; |
| /* TSID, fragment bit and coding of length(in 2 length octets following) */ |
| *p++ = (p_scb->curr_cfg.mux_tsid_media<<3) | (AVDT_ALH_FRAG_MASK|AVDT_ALH_LCODE_16BIT); |
| |
| /* length of all remaining transport packet */ |
| UINT16_TO_BE_STREAM(p, p_frag->layer_specific ); |
| } |
| /* put fragment into gueue */ |
| GKI_enqueue(pq, p_frag); |
| num_frag--; |
| } |
| } |
| #endif |
| |