diff options
author | Andre Eisenbach <andre@broadcom.com> | 2012-02-22 13:18:21 -0800 |
---|---|---|
committer | Matthew Xie <mattx@google.com> | 2012-07-14 11:19:11 -0700 |
commit | e448862a47c08eb23185aaed574b39264f5005fc (patch) | |
tree | 2bc6246e3091315e77224fd798ea2fe8074ef972 /stack/avdt/avdt_scb_act.c | |
parent | a2ca4b83ab8bbbfd8d5f6693e927ed4b82094624 (diff) | |
download | external_bluetooth_bluedroid-e448862a47c08eb23185aaed574b39264f5005fc.zip external_bluetooth_bluedroid-e448862a47c08eb23185aaed574b39264f5005fc.tar.gz external_bluetooth_bluedroid-e448862a47c08eb23185aaed574b39264f5005fc.tar.bz2 |
Initial Bluedroid stack commit
Diffstat (limited to 'stack/avdt/avdt_scb_act.c')
-rw-r--r-- | stack/avdt/avdt_scb_act.c | 2111 |
1 files changed, 2111 insertions, 0 deletions
diff --git a/stack/avdt/avdt_scb_act.c b/stack/avdt/avdt_scb_act.c new file mode 100644 index 0000000..1784713 --- /dev/null +++ b/stack/avdt/avdt_scb_act.c @@ -0,0 +1,2111 @@ +/***************************************************************************** +** +** Name: avdt_scb_act.c +** +** Description: This module contains the action functions associated +** with the stream control block state machine. +** +** Copyright (c) 2002-2008, Broadcom Corp., All Rights Reserved. +** WIDCOMM Bluetooth Core. Proprietary and confidential. +** +*****************************************************************************/ + +#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 */ + + 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 + |