/******************************************************************************
*
* 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 "bt_utils.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)
{
UNUSED(p_scb);
UNUSED(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)
{
UNUSED(p_scb);
UNUSED(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)
{
UNUSED(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_WARNING("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_DEBUG( "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_WARNING( " - 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_ERROR( "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_WARNING("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_WARNING("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_WARNING("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_DEBUG("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_WARNING("bad al_len: %d(<12)", al_len);
break;
}
/* check that data fit into buffer */
if (al_len > p_scb->media_buf_len)
{
AVDT_TRACE_WARNING("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_WARNING("bad al_len: %d(>%d)", al_len, p_scb->media_buf_len);
break;
}
}
else
{
AVDT_TRACE_DEBUG("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_WARNING("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_WARNING("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_WARNING("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_DEBUG("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_WARNING("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_WARNING("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_DEBUG("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_WARNING("*** 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)
{
UNUSED(p_scb);
GKI_freebuf(p_data->p_pkt);
AVDT_TRACE_ERROR(" avdt_scb_drop_pkt 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), /* handle of scb- which is same as sep handle of bta_av_cb.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;
UNUSED(p_data);
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)
{
UNUSED(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)
{
UNUSED(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_DEBUG("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_WARNING("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_scb->media_seq++;
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);
}
/* 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_WARNING("*** 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;
UNUSED(p_data);
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)
{
UNUSED(p_scb);
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;
UNUSED(p_data);
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_WARNING("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;
UNUSED(p_data);
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;
UNUSED(p_data);
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)
{
UNUSED(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;
UNUSED(p_data);
/* 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)
{
UNUSED(p_scb);
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)
{
UNUSED(p_scb);
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)
{
UNUSED(p_scb);
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)
{
UNUSED(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_WARNING("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
UNUSED(p_data);
/* 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_DEBUG("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_DEBUG("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
UNUSED(p_data);
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_DEBUG("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_DEBUG("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_DEBUG("avdt/l2c congested!!");
break;/* exit loop if channel became congested */
}
}
AVDT_TRACE_DEBUG("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)
{
UNUSED(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)
{
UNUSED(p_data);
if ((p_scb->cs.tsep == AVDT_TSEP_SNK) && (!p_scb->sink_activated))
{
p_scb->in_use = TRUE;
}
else
{
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_DEBUG("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_DEBUG("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_WARNING("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_DEBUG("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