/******************************************************************************
 *
 *  Copyright (C) 2002-2012 Broadcom Corporation
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at:
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 ******************************************************************************/

/******************************************************************************
 *
 *  This module contains the action functions associated with the stream
 *  control block state machine.
 *
 ******************************************************************************/

#include <string.h>
#include "data_types.h"
#include "bt_target.h"
#include "avdt_api.h"
#include "avdtc_api.h"
#include "avdt_int.h"
#include "gki.h"
#include "btu.h"

/* This table is used to lookup the callback event that matches a particular
** state machine API request event.  Note that state machine API request
** events are at the beginning of the event list starting at zero, thus
** allowing for this table.
*/
const UINT8 avdt_scb_cback_evt[] = {
    0,                          /* API_REMOVE_EVT (no event) */
    AVDT_WRITE_CFM_EVT,         /* API_WRITE_REQ_EVT */
    0,                          /* API_GETCONFIG_REQ_EVT (no event) */
    0,                          /* API_DELAY_RPT_REQ_EVT (no event) */
    AVDT_OPEN_CFM_EVT,          /* API_SETCONFIG_REQ_EVT */
    AVDT_OPEN_CFM_EVT,          /* API_OPEN_REQ_EVT */
    AVDT_CLOSE_CFM_EVT,         /* API_CLOSE_REQ_EVT */
    AVDT_RECONFIG_CFM_EVT,      /* API_RECONFIG_REQ_EVT */
    AVDT_SECURITY_CFM_EVT,      /* API_SECURITY_REQ_EVT */
    0                           /* API_ABORT_REQ_EVT (no event) */
};

/* This table is used to look up the callback event based on the signaling
** role when the stream is closed.
*/
const UINT8 avdt_scb_role_evt[] = {
    AVDT_CLOSE_IND_EVT,         /* AVDT_CLOSE_ACP */
    AVDT_CLOSE_CFM_EVT,         /* AVDT_CLOSE_INT */
    AVDT_CLOSE_IND_EVT,         /* AVDT_OPEN_ACP */
    AVDT_OPEN_CFM_EVT           /* AVDT_OPEN_INT */
};

/*******************************************************************************
**
** Function         avdt_scb_gen_ssrc
**
** Description      This function generates a SSRC number unique to the stream.
**
** Returns          SSRC value.
**
*******************************************************************************/
UINT32 avdt_scb_gen_ssrc(tAVDT_SCB *p_scb)
{
    /* combine the value of the media type and codec type of the SCB */
    return ((UINT32)(p_scb->cs.cfg.codec_info[1] | p_scb->cs.cfg.codec_info[2]));
}

/*******************************************************************************
**
** Function         avdt_scb_hdl_abort_cmd
**
** Description      This function sends the SCB an AVDT_SCB_API_ABORT_RSP_EVT
**                  to initiate sending of an abort response message.
**
** Returns          Nothing.
**
*******************************************************************************/
void avdt_scb_hdl_abort_cmd(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
{
    p_scb->role = AVDT_CLOSE_ACP;
    avdt_scb_event(p_scb, AVDT_SCB_API_ABORT_RSP_EVT, p_data);
}

/*******************************************************************************
**
** Function         avdt_scb_hdl_abort_rsp
**
** Description      This function is an empty function; it serves as a
**                  placeholder for a conformance API action function.
**
** Returns          Nothing.
**
*******************************************************************************/
void avdt_scb_hdl_abort_rsp(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
{
    return;
}

/*******************************************************************************
**
** Function         avdt_scb_hdl_close_cmd
**
** Description      This function sends the SCB an AVDT_SCB_API_CLOSE_RSP_EVT
**                  to initiate sending of a close response message.
**
** Returns          Nothing.
**
*******************************************************************************/
void avdt_scb_hdl_close_cmd(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
{
    p_scb->role = AVDT_CLOSE_ACP;
    avdt_scb_event(p_scb, AVDT_SCB_API_CLOSE_RSP_EVT, p_data);
}

/*******************************************************************************
**
** Function         avdt_scb_hdl_close_rsp
**
** Description      This function sets the close_code variable to the error
**                  code returned in the close response.
**
** Returns          Nothing.
**
*******************************************************************************/
void avdt_scb_hdl_close_rsp(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
{
    p_scb->close_code = p_data->msg.hdr.err_code;
}

/*******************************************************************************
**
** Function         avdt_scb_hdl_getconfig_cmd
**
** Description      This function retrieves the configuration parameters of
**                  the SCB and sends the SCB an AVDT_SCB_API_GETCONFIG_RSP_EVT
**                  to initiate sending of a get configuration response message.
**
** Returns          Nothing.
**
*******************************************************************************/
void avdt_scb_hdl_getconfig_cmd(tAVDT_SCB *p_scb,tAVDT_SCB_EVT *p_data)
{
    p_data->msg.svccap.p_cfg = &p_scb->curr_cfg;

    avdt_scb_event(p_scb, AVDT_SCB_API_GETCONFIG_RSP_EVT, p_data);
}

/*******************************************************************************
**
** Function         avdt_scb_hdl_getconfig_rsp
**
** Description      This function is an empty function; it serves as a
**                  placeholder for a conformance API action function.
**
** Returns          Nothing.
**
*******************************************************************************/
void avdt_scb_hdl_getconfig_rsp(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
{
    return;
}

/*******************************************************************************
**
** Function         avdt_scb_hdl_open_cmd
**
** Description      This function sends the SCB an AVDT_SCB_API_OPEN_RSP_EVT
**                  to initiate sending of an open response message.
**
** Returns          Nothing.
**
*******************************************************************************/
void avdt_scb_hdl_open_cmd(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
{
    avdt_scb_event(p_scb, AVDT_SCB_API_OPEN_RSP_EVT, p_data);
}

/*******************************************************************************
**
** Function         avdt_scb_hdl_open_rej
**
** Description      This function calls the application callback function
**                  indicating the open request has failed.  It initializes
**                  certain SCB variables and sends a AVDT_CCB_UL_CLOSE_EVT
**                  to the CCB.
**
** Returns          Nothing.
**
*******************************************************************************/
void avdt_scb_hdl_open_rej(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
{
    /* do exactly same as setconfig reject */
    avdt_scb_hdl_setconfig_rej(p_scb, p_data);
}

/*******************************************************************************
**
** Function         avdt_scb_hdl_open_rsp
**
** Description      This function calls avdt_ad_open_req() to initiate
**                  connection of the transport channel for this stream.
**
** Returns          Nothing.
**
*******************************************************************************/
void avdt_scb_hdl_open_rsp(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
{
    /* initiate opening of trans channels for this SEID */
    p_scb->role = AVDT_OPEN_INT;
    avdt_ad_open_req(AVDT_CHAN_MEDIA, p_scb->p_ccb, p_scb, AVDT_INT);

    /* start tc connect timer */
    btu_start_timer(&p_scb->timer_entry, BTU_TTYPE_AVDT_SCB_TC, AVDT_SCB_TC_CONN_TOUT);
}

/*******************************************************************************
**
** Function         avdt_scb_hdl_pkt_no_frag
**
** Description
**
** Returns          Nothing.
**
*******************************************************************************/
void avdt_scb_hdl_pkt_no_frag(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
{
    UINT8   *p, *p_start;
    UINT8   o_v, o_p, o_x, o_cc;
    UINT8   m_pt;
    UINT8   marker;
    UINT16  seq;
    UINT32  time_stamp;
    UINT32  ssrc;
    UINT16  offset;
    UINT16  ex_len;
    UINT8   pad_len = 0;

    p = p_start = (UINT8 *)(p_data->p_pkt + 1) + p_data->p_pkt->offset;

    /* parse media packet header */
    AVDT_MSG_PRS_OCTET1(p, o_v, o_p, o_x, o_cc);
    AVDT_MSG_PRS_M_PT(p, m_pt, marker);
    BE_STREAM_TO_UINT16(seq, p);
    BE_STREAM_TO_UINT32(time_stamp, p);
    BE_STREAM_TO_UINT32(ssrc, p);

    /* skip over any csrc's in packet */
    p += o_cc * 4;

    /* check for and skip over extension header */
    if (o_x)
    {
        p += 2;
        BE_STREAM_TO_UINT16(ex_len, p);
        p += ex_len * 4;
    }

    /* save our new offset */
    offset = (UINT16) (p - p_start);

    /* adjust length for any padding at end of packet */
    if (o_p)
    {
        /* padding length in last byte of packet */
        pad_len =  *(p_start + p_data->p_pkt->len);
    }

    /* do sanity check */
    if ((offset > p_data->p_pkt->len) || ((pad_len + offset) > p_data->p_pkt->len))
    {
        AVDT_TRACE_WARNING0("Got bad media packet");
        GKI_freebuf(p_data->p_pkt);
    }
    /* adjust offset and length and send it up */
    else
    {
        p_data->p_pkt->len -= (offset + pad_len);
        p_data->p_pkt->offset += offset;

        if (p_scb->cs.p_data_cback != NULL)
        {
            /* report sequence number */
            p_data->p_pkt->layer_specific = seq;
            (*p_scb->cs.p_data_cback)(avdt_scb_to_hdl(p_scb), p_data->p_pkt,
                time_stamp, (UINT8)(m_pt | (marker<<7)));
        }
        else
        {
#if AVDT_MULTIPLEXING == TRUE
            if ((p_scb->cs.p_media_cback != NULL)
             && (p_scb->p_media_buf != NULL)
             && (p_scb->media_buf_len > p_data->p_pkt->len))
            {
                /* media buffer enough length is assigned by application. Lets use it*/
                memcpy(p_scb->p_media_buf,(UINT8*)(p_data->p_pkt + 1) + p_data->p_pkt->offset,
                    p_data->p_pkt->len);
                (*p_scb->cs.p_media_cback)(avdt_scb_to_hdl(p_scb),p_scb->p_media_buf,
                    p_scb->media_buf_len,time_stamp,seq,m_pt,marker);
            }
#endif
            GKI_freebuf(p_data->p_pkt);
        }
    }
}

#if AVDT_REPORTING == TRUE
/*******************************************************************************
**
** Function         avdt_scb_hdl_report
**
** Description
**
** Returns          Nothing.
**
*******************************************************************************/
UINT8 * avdt_scb_hdl_report(tAVDT_SCB *p_scb, UINT8 *p, UINT16 len)
{
    UINT16  result = AVDT_SUCCESS;
    UINT8   *p_start = p;
    UINT32  ssrc;
    UINT8   o_v, o_p, o_cc;
    UINT16  pkt_len;
    AVDT_REPORT_TYPE    pt;
    tAVDT_REPORT_DATA   report, *p_rpt;

    AVDT_TRACE_DEBUG0( "avdt_scb_hdl_report");
    if(p_scb->cs.p_report_cback)
    {
        p_rpt = &report;
        /* parse report packet header */
        AVDT_MSG_PRS_RPT_OCTET1(p, o_v, o_p, o_cc);
        pt = *p++;
        BE_STREAM_TO_UINT16(pkt_len, p);
        BE_STREAM_TO_UINT32(ssrc, p);

        switch(pt)
        {
        case AVDT_RTCP_PT_SR:   /* the packet type - SR (Sender Report) */
            BE_STREAM_TO_UINT32(report.sr.ntp_sec, p);
            BE_STREAM_TO_UINT32(report.sr.ntp_frac, p);
            BE_STREAM_TO_UINT32(report.sr.rtp_time, p);
            BE_STREAM_TO_UINT32(report.sr.pkt_count, p);
            BE_STREAM_TO_UINT32(report.sr.octet_count, p);
            break;

        case AVDT_RTCP_PT_RR:   /* the packet type - RR (Receiver Report) */
            report.rr.frag_lost = *p;
            BE_STREAM_TO_UINT32(report.rr.packet_lost, p);
            report.rr.packet_lost &= 0xFFFFFF;
            BE_STREAM_TO_UINT32(report.rr.seq_num_rcvd, p);
            BE_STREAM_TO_UINT32(report.rr.jitter, p);
            BE_STREAM_TO_UINT32(report.rr.lsr, p);
            BE_STREAM_TO_UINT32(report.rr.dlsr, p);
            break;

        case AVDT_RTCP_PT_SDES: /* the packet type - SDES (Source Description) */
            if(*p == AVDT_RTCP_SDES_CNAME)
            {
                p_rpt = (tAVDT_REPORT_DATA *)(p+2);
            }
            else
            {
                AVDT_TRACE_WARNING5( " - SDES SSRC=0x%08x sc=%d %d len=%d %s",
                    ssrc, o_cc, *p, *(p+1), p+2);
                result = AVDT_BUSY;
            }
            break;

        default:
            AVDT_TRACE_ERROR1( "Bad Report pkt - packet type: %d", pt);
            result = AVDT_BAD_PARAMS;
        }

        if(result == AVDT_SUCCESS)
            (*p_scb->cs.p_report_cback)(avdt_scb_to_hdl(p_scb), pt, p_rpt);

    }
    p_start += len;
    return p_start;
}
#endif

#if AVDT_MULTIPLEXING == TRUE
/*******************************************************************************
**
** Function         avdt_scb_hdl_pkt_frag
**
** Description
**
** Returns          Nothing.
**
*******************************************************************************/
void avdt_scb_hdl_pkt_frag(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
{
    /* Fields of Adaptation Layer Header */
    UINT8   al_tsid,al_frag,al_lcode;
    UINT16  al_len;
    /* media header fields */
    UINT8   o_v, o_p, o_x, o_cc;
    UINT8   m_pt;
    UINT8   marker;
    UINT16  seq;
    UINT32  time_stamp;
    UINT32  ssrc;
    UINT16  ex_len;
    UINT8   pad_len;
    /* other variables */
    UINT8   *p; /* current pointer */
    UINT8   *p_end; /* end of all packet */
    UINT8   *p_payload; /* pointer to media fragment payload in the buffer */
    UINT32  payload_len; /* payload length */
    UINT16  frag_len; /* fragment length */

    p = (UINT8 *)(p_data->p_pkt + 1) + p_data->p_pkt->offset;
    p_end = p + p_data->p_pkt->len;
    /* parse all fragments */
    while(p < p_end)
    {
        if (p_end - p < 4) /* length check. maximum length of AL header = 4 */
        {
            AVDT_TRACE_WARNING2("p_end: 0x%x - p:0x%x < 4", p_end, p);
            break;
        }

        /* parse first byte */
        al_tsid = (*p)>>3;
        al_frag = ( (*p) >> 2 ) & 0x01;
        al_lcode = (*p++) & AVDT_ALH_LCODE_MASK;

        /* in case of TSID=00000, a second AL header byte, before the length field,
        ** is expected and contains the actual TSID, aligned with MSB */
        if(al_tsid == 0)
            al_tsid = *p++;

        /* get remaining media length on base of lcode */
        switch(al_lcode)
        {
        case AVDT_ALH_LCODE_NONE:  /* No length field present. Take length from l2cap */
            al_len = (UINT16)(p_end - p);
            break;
        case AVDT_ALH_LCODE_16BIT:  /* 16 bit length field */
            BE_STREAM_TO_UINT16(al_len, p);
            break;
        case AVDT_ALH_LCODE_9BITM0:  /* 9 bit length field, MSB = 0, 8 LSBs in 1 octet following */
            al_len = *p++;
            break;
        default:    /* 9 bit length field, MSB = 1, 8 LSBs in 1 octet following */
            al_len =(UINT16)*p++ + 0x100;
        }

        /* max fragment length */
        frag_len = (UINT16)(p_end - p);
        /* if it isn't last fragment */
        if(frag_len >= al_len)
            frag_len = al_len;

        /* check TSID corresponds to config */
        if (al_tsid != p_scb->curr_cfg.mux_tsid_media)
        {
#if AVDT_REPORTING == TRUE
            if((p_scb->curr_cfg.psc_mask & AVDT_PSC_REPORT) &&
                (al_tsid == p_scb->curr_cfg.mux_tsid_report))
            {
                /* parse reporting packet */
                p = avdt_scb_hdl_report(p_scb, p, frag_len);
                continue;
            }
            else
#endif
            {
                AVDT_TRACE_WARNING2("bad tsid: %d, mux_tsid_media:%d", al_tsid, p_scb->curr_cfg.mux_tsid_media);
                break;
            }
        }
        /* check are buffer for assembling and related callback set */
        else if ((p_scb->p_media_buf == NULL) || (p_scb->cs.p_media_cback == NULL))
        {
            AVDT_TRACE_WARNING0("NULL p_media_buf or p_media_cback");
            break;
        }


        /* it is media fragment beginning */
        if(!al_frag) /* is it first fragment of original media packet */
        {
            AVDT_TRACE_DEBUG2("al:%d media:%d",
                al_len, p_scb->media_buf_len);

            p_scb->frag_off = 0;
            p_scb->frag_org_len = al_len; /* total length of original media packet */
            /* length check: minimum length of media header is 12 */
            if (p_scb->frag_org_len < 12)
            {
                AVDT_TRACE_WARNING1("bad al_len: %d(<12)", al_len);
                break;
            }
            /* check that data fit into buffer */
            if (al_len > p_scb->media_buf_len)
            {
                AVDT_TRACE_WARNING2("bad al_len: %d(>%d)", al_len, p_scb->media_buf_len);
                break;
            }
            /* make sure it is the last fragment in l2cap packet */
            if (p + al_len < p_end)
            {
                AVDT_TRACE_WARNING2("bad al_len: %d(>%d)", al_len, p_scb->media_buf_len);
                break;
            }
        }
        else
        {
            AVDT_TRACE_DEBUG4("al:%d media:%d frag_org_len:%d frag_off:%d",
                al_len, p_scb->media_buf_len, p_scb->frag_org_len, p_scb->frag_off);

            /* check that remaining length from AL header equals to original len - length of already received fragments */
            if(al_len != p_scb->frag_org_len - p_scb->frag_off)
            {
                AVDT_TRACE_WARNING4("al_len:%d != (frag_org_len:%d - frag_off:%d) %d",
                    al_len, p_scb->frag_org_len, p_scb->frag_off,
                    (p_scb->frag_org_len- p_scb->frag_off));
                break;
            }

            /* do sanity check */
            if (p_scb->frag_off == 0)
            {
                AVDT_TRACE_WARNING0("frag_off=0");
                break;
            }
        }
        /* do common sanity check */
        if((p_scb->frag_org_len <= p_scb->frag_off) || (p_scb->frag_org_len >= p_scb->media_buf_len))
        {
            AVDT_TRACE_WARNING3("common sanity frag_off:%d frag_org_len:%d media_buf_len:%d",
                p_scb->frag_off, p_scb->frag_org_len, p_scb->media_buf_len);
            break;
        }

        AVDT_TRACE_DEBUG4("Received fragment org_len=%d off=%d al_len=%d frag_len=%d",
            p_scb->frag_org_len, p_scb->frag_off, al_len, frag_len);

        /* copy fragment into buffer */
        memcpy(p_scb->p_media_buf + p_scb->frag_off, p, frag_len);
        p_scb->frag_off += frag_len;
        /* move to the next fragment */
        p += frag_len;
        /* if it is last fragment in original media packet then process total media pocket */
        if(p_scb->frag_off == p_scb->frag_org_len)
        {
            p_payload = p_scb->p_media_buf;

            /* media header */
            AVDT_MSG_PRS_OCTET1(p_payload, o_v, o_p, o_x, o_cc);
            AVDT_MSG_PRS_M_PT(p_payload, m_pt, marker);
            BE_STREAM_TO_UINT16(seq, p_payload);
            BE_STREAM_TO_UINT32(time_stamp, p_payload);
            BE_STREAM_TO_UINT32(ssrc, p_payload);

            /* skip over any csrc's in packet */
            p_payload += o_cc * 4;

            /* check for and skip over extension header */
            if (o_x)
            {
                if(p_scb->p_media_buf + p_scb->frag_off - p_payload < 4)
                {
                    AVDT_TRACE_WARNING3("length check frag_off:%d p_media_buf:%d p_payload:%d",
                        p_scb->frag_off, p_scb->p_media_buf, p_payload);
                    break;/* length check */
                }
                p_payload += 2;
                BE_STREAM_TO_UINT16(ex_len, p_payload);
                p_payload += ex_len * 4;
            }

            if(p_payload >= p_scb->p_media_buf + p_scb->frag_off)
            {
                AVDT_TRACE_WARNING3("length check2 frag_off:%d p_media_buf:%d p_payload:%d",
                    p_scb->frag_off, p_scb->p_media_buf, p_payload);
                break;/* length check */
            }

            /* adjust length for any padding at end of packet */
            if (o_p)
            {
                /* padding length in last byte of packet */
                pad_len =  *(p_scb->p_media_buf + p_scb->frag_off - 1);
            }
            else
                pad_len =  0;
            /* payload length */
            payload_len = (UINT32)(p_scb->p_media_buf + p_scb->frag_off - pad_len - p_payload);

            AVDT_TRACE_DEBUG2("Received last fragment header=%d len=%d",
                p_payload - p_scb->p_media_buf,payload_len);

            /* send total media packet up */
            if (p_scb->cs.p_media_cback != NULL)
            {
                (*p_scb->cs.p_media_cback)(avdt_scb_to_hdl(p_scb), p_payload,
                                           payload_len, time_stamp, seq, m_pt, marker);
            }
        }
    } /* while(p < p_end) */

    if(p < p_end)
    {
        AVDT_TRACE_WARNING0("*** Got bad media packet");
    }
    GKI_freebuf(p_data->p_pkt);
}
#endif

/*******************************************************************************
**
** Function         avdt_scb_hdl_pkt
**
** Description
**
** Returns          Nothing.
**
*******************************************************************************/
void avdt_scb_hdl_pkt(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
{
#if AVDT_REPORTING == TRUE
    UINT8 *p;
#endif

#if AVDT_MULTIPLEXING == TRUE
    /* select right function in dependance of is fragmentation supported or not */
    if( 0 != (p_scb->curr_cfg.psc_mask & AVDT_PSC_MUX))
    {
        avdt_scb_hdl_pkt_frag(p_scb, p_data);
    }
    else
#endif
#if AVDT_REPORTING == TRUE
    if(p_data->p_pkt->layer_specific == AVDT_CHAN_REPORT)
    {
        p = (UINT8 *)(p_data->p_pkt + 1) + p_data->p_pkt->offset;
        avdt_scb_hdl_report(p_scb, p, p_data->p_pkt->len);
        GKI_freebuf(p_data->p_pkt);
    }
    else
#endif
        avdt_scb_hdl_pkt_no_frag(p_scb, p_data);
}

/*******************************************************************************
**
** Function         avdt_scb_drop_pkt
**
** Description      Drop an incoming media packet.  This function is called if
**                  a media packet is received in any state besides streaming.
**
** Returns          Nothing.
**
*******************************************************************************/
void avdt_scb_drop_pkt(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
{
    GKI_freebuf(p_data->p_pkt);
    AVDT_TRACE_WARNING0("Dropped incoming media packet");
}

/*******************************************************************************
**
** Function         avdt_scb_hdl_reconfig_cmd
**
** Description      This function calls the application callback function
**                  with a reconfiguration indication.
**
** Returns          Nothing.
**
*******************************************************************************/
void avdt_scb_hdl_reconfig_cmd(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
{
    /* if command not supported */
    if (p_scb->cs.nsc_mask & AVDT_NSC_RECONFIG)
    {
        /* send reject */
        p_data->msg.hdr.err_code = AVDT_ERR_NSC;
        p_data->msg.hdr.err_param = 0;
        avdt_scb_event(p_scb, AVDT_SCB_API_RECONFIG_RSP_EVT, p_data);
    }
    else
    {
        /* store requested configuration */
        memcpy(&p_scb->req_cfg, p_data->msg.reconfig_cmd.p_cfg, sizeof(tAVDT_CFG));

        /* call application callback */
        (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb),
                                  NULL,
                                  AVDT_RECONFIG_IND_EVT,
                                  (tAVDT_CTRL *) &p_data->msg.reconfig_cmd);
    }
}

/*******************************************************************************
**
** Function         avdt_scb_hdl_reconfig_rsp
**
** Description      This function calls the application callback function
**                  with a reconfiguration confirm.
**
** Returns          Nothing.
**
*******************************************************************************/
void avdt_scb_hdl_reconfig_rsp(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
{
    if (p_data->msg.hdr.err_code == 0)
    {
        /* store new configuration */
        if (p_scb->req_cfg.num_codec > 0)
        {
            p_scb->curr_cfg.num_codec = p_scb->req_cfg.num_codec;
            memcpy(p_scb->curr_cfg.codec_info, p_scb->req_cfg.codec_info, AVDT_CODEC_SIZE);
        }
        if (p_scb->req_cfg.num_protect > 0)
        {
            p_scb->curr_cfg.num_protect = p_scb->req_cfg.num_protect;
            memcpy(p_scb->curr_cfg.protect_info, p_scb->req_cfg.protect_info, AVDT_PROTECT_SIZE);
        }
    }

    p_data->msg.svccap.p_cfg = &p_scb->curr_cfg;

    /* call application callback */
    (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb),
                              NULL,
                              AVDT_RECONFIG_CFM_EVT,
                              (tAVDT_CTRL *) &p_data->msg.svccap);
}

/*******************************************************************************
**
** Function         avdt_scb_hdl_security_cmd
**
** Description      This function calls the application callback with a
**                  security indication.
**
** Returns          Nothing.
**
*******************************************************************************/
void avdt_scb_hdl_security_cmd(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
{
    /* if command not supported */
    if (p_scb->cs.nsc_mask & AVDT_NSC_SECURITY)
    {
        /* send reject */
        p_data->msg.hdr.err_code = AVDT_ERR_NSC;
        avdt_scb_event(p_scb, AVDT_SCB_API_SECURITY_RSP_EVT, p_data);
    }
    else
    {
        /* call application callback */
        (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb),
                                  NULL,
                                  AVDT_SECURITY_IND_EVT,
                                  (tAVDT_CTRL *) &p_data->msg.security_cmd);
    }
}

/*******************************************************************************
**
** Function         avdt_scb_hdl_security_rsp
**
** Description      This function calls the application callback with a
**                  security confirm.
**
** Returns          Nothing.
**
*******************************************************************************/
void avdt_scb_hdl_security_rsp(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
{
    /* call application callback */
    (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb),
                              NULL,
                              AVDT_SECURITY_CFM_EVT,
                              (tAVDT_CTRL *) &p_data->msg.security_cmd);
}

/*******************************************************************************
**
** Function         avdt_scb_hdl_setconfig_cmd
**
** Description      This function marks the SCB as in use and copies the
**                  configuration and peer SEID to the SCB.  It then calls
**                  the application callback with a configuration indication.
**
** Returns          Nothing.
**
*******************************************************************************/
void avdt_scb_hdl_setconfig_cmd(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
{
    tAVDT_CFG *p_cfg;

    if (!p_scb->in_use)
    {
        p_cfg = p_data->msg.config_cmd.p_cfg;
        if(p_scb->cs.cfg.codec_info[AVDT_CODEC_TYPE_INDEX] == p_cfg->codec_info[AVDT_CODEC_TYPE_INDEX])
        {
            /* set sep as in use */
            p_scb->in_use = TRUE;

            /* copy info to scb */
            p_scb->p_ccb = avdt_ccb_by_idx(p_data->msg.config_cmd.hdr.ccb_idx);
            p_scb->peer_seid = p_data->msg.config_cmd.int_seid;
            memcpy(&p_scb->req_cfg, p_cfg, sizeof(tAVDT_CFG));
            /* call app callback */
            (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb),
                                      p_scb->p_ccb ? p_scb->p_ccb->peer_addr : NULL,
                                      AVDT_CONFIG_IND_EVT,
                                      (tAVDT_CTRL *) &p_data->msg.config_cmd);
        }
        else
        {
            p_data->msg.hdr.err_code = AVDT_ERR_UNSUP_CFG;
            p_data->msg.hdr.err_param = 0;
            avdt_msg_send_rej(avdt_ccb_by_idx(p_data->msg.hdr.ccb_idx),
                              p_data->msg.hdr.sig_id, &p_data->msg);
        }
    }
    else
    {
        avdt_scb_rej_in_use(p_scb, p_data);
    }
}

/*******************************************************************************
**
** Function         avdt_scb_hdl_setconfig_rej
**
** Description      This function marks the SCB as not in use and calls the
**                  application callback with an open confirm indicating failure.
**
** Returns          Nothing.
**
*******************************************************************************/
void avdt_scb_hdl_setconfig_rej(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
{
    /* clear scb variables */
    avdt_scb_clr_vars(p_scb, p_data);

    /* tell ccb we're done with signaling channel */
    avdt_ccb_event(avdt_ccb_by_idx(p_data->msg.hdr.ccb_idx), AVDT_CCB_UL_CLOSE_EVT, NULL);

    /* call application callback */
    (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb),
                              NULL,
                              AVDT_OPEN_CFM_EVT,
                              (tAVDT_CTRL *) &p_data->msg.hdr);
}

/*******************************************************************************
**
** Function         avdt_scb_hdl_setconfig_rsp
**
** Description      This function sends the SCB an AVDT_SCB_API_OPEN_REQ_EVT
**                  to initiate sending of an open command message.
**
** Returns          Nothing.
**
*******************************************************************************/
void avdt_scb_hdl_setconfig_rsp(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
{
    tAVDT_EVT_HDR   single;

    if (p_scb->p_ccb != NULL)
    {
        /* save configuration */
        memcpy(&p_scb->curr_cfg, &p_scb->req_cfg, sizeof(tAVDT_CFG));

        /* initiate open */
        single.seid = p_scb->peer_seid;
        avdt_scb_event(p_scb, AVDT_SCB_API_OPEN_REQ_EVT, (tAVDT_SCB_EVT *) &single);
    }
}

/*******************************************************************************
**
** Function         avdt_scb_hdl_start_cmd
**
** Description      This function calls the application callback with a
**                  start indication.
**
** Returns          Nothing.
**
*******************************************************************************/
void avdt_scb_hdl_start_cmd(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
{
    (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb),
                              p_scb->p_ccb ? p_scb->p_ccb->peer_addr : NULL,
                              AVDT_START_IND_EVT,
                              NULL);
}

/*******************************************************************************
**
** Function         avdt_scb_hdl_start_rsp
**
** Description      This function calls the application callback with a
**                  start confirm.
**
** Returns          Nothing.
**
*******************************************************************************/
void avdt_scb_hdl_start_rsp(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
{
    (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb),
                              p_scb->p_ccb ? p_scb->p_ccb->peer_addr : NULL,
                              AVDT_START_CFM_EVT,
                              (tAVDT_CTRL *) &p_data->msg.hdr);
}

/*******************************************************************************
**
** Function         avdt_scb_hdl_suspend_cmd
**
** Description      This function calls the application callback with a suspend
**                  indication.
**
** Returns          Nothing.
**
*******************************************************************************/
void avdt_scb_hdl_suspend_cmd(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
{
    (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb),
                              p_scb->p_ccb ? p_scb->p_ccb->peer_addr : NULL,
                              AVDT_SUSPEND_IND_EVT,
                              NULL);
}

/*******************************************************************************
**
** Function         avdt_scb_hdl_suspend_rsp
**
** Description      This function calls the application callback with a suspend
**                  confirm.
**
** Returns          Nothing.
**
*******************************************************************************/
void avdt_scb_hdl_suspend_rsp(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
{
    (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb),
                              p_scb->p_ccb ? p_scb->p_ccb->peer_addr : NULL,
                              AVDT_SUSPEND_CFM_EVT,
                              (tAVDT_CTRL *) &p_data->msg.hdr);
}

/*******************************************************************************
**
** Function         avdt_scb_hdl_tc_close
**
** Description      This function is called when the transport channel is
**                  closed.  It marks the SCB as not in use and
**                  initializes certain SCB parameters.  It then sends
**                  an AVDT_CCB_UL_CLOSE_EVT to the CCB if the SCB
**                  initiated the close.  It then checks to see if the SCB
**                  is to be removed.  If it is it deallocates the SCB.  Finally,
**                  it calls the application callback with a close indication.
**
** Returns          Nothing.
**
*******************************************************************************/
void avdt_scb_hdl_tc_close(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
{
    UINT8               hdl = avdt_scb_to_hdl(p_scb);
    tAVDT_CTRL_CBACK    *p_ctrl_cback = p_scb->cs.p_ctrl_cback;
    tAVDT_CTRL          avdt_ctrl;
    UINT8               event;
    tAVDT_CCB           *p_ccb = p_scb->p_ccb;
    BD_ADDR remote_addr;


    memcpy (remote_addr, p_ccb->peer_addr, BD_ADDR_LEN);

    /* set up hdr */
    avdt_ctrl.hdr.err_code = p_scb->close_code;

    /* clear sep variables */
    avdt_scb_clr_vars(p_scb, p_data);
    p_scb->media_seq = 0;
    p_scb->cong = FALSE;

    /* free pkt we're holding, if any */
    if (p_scb->p_pkt != NULL)
    {
        GKI_freebuf(p_scb->p_pkt);
        p_scb->p_pkt = NULL;
    }

    /* stop transport channel timer */
    btu_stop_timer(&p_scb->timer_entry);

    if ((p_scb->role == AVDT_CLOSE_INT) || (p_scb->role == AVDT_OPEN_INT))
    {
        /* tell ccb we're done with signaling channel */
        avdt_ccb_event(p_ccb, AVDT_CCB_UL_CLOSE_EVT, NULL);
    }
    event = (p_scb->role == AVDT_CLOSE_INT) ? AVDT_CLOSE_CFM_EVT : AVDT_CLOSE_IND_EVT;
    p_scb->role = AVDT_CLOSE_ACP;

    if (p_scb->remove)
    {
        avdt_scb_dealloc(p_scb, NULL);
    }

    /* call app callback */
    (*p_ctrl_cback)(hdl, remote_addr, event, &avdt_ctrl);
}

/*******************************************************************************
**
** Function         avdt_scb_snd_delay_rpt_req
**
** Description      This function calls the application callback with a delay
**                  report.
**
** Returns          Nothing.
**
*******************************************************************************/
void avdt_scb_snd_delay_rpt_req (tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
{
    avdt_msg_send_cmd(p_scb->p_ccb, p_scb, AVDT_SIG_DELAY_RPT, (tAVDT_MSG *) &p_data->apidelay);
}

/*******************************************************************************
**
** Function         avdt_scb_hdl_delay_rpt_cmd
**
** Description      This function calls the application callback with a delay
**                  report.
**
** Returns          Nothing.
**
*******************************************************************************/
void avdt_scb_hdl_delay_rpt_cmd (tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
{
    (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb),
                              p_scb->p_ccb ? p_scb->p_ccb->peer_addr : NULL,
                              AVDT_DELAY_REPORT_EVT,
                              (tAVDT_CTRL *) &p_data->msg.hdr);

    if (p_scb->p_ccb)
        avdt_msg_send_rsp(p_scb->p_ccb, AVDT_SIG_DELAY_RPT, &p_data->msg);
    else
        avdt_scb_rej_not_in_use(p_scb, p_data);
}

/*******************************************************************************
**
** Function         avdt_scb_hdl_delay_rpt_rsp
**
** Description      This function calls the application callback with a delay
**                  report.
**
** Returns          Nothing.
**
*******************************************************************************/
void avdt_scb_hdl_delay_rpt_rsp (tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
{
    (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb),
                              p_scb->p_ccb ? p_scb->p_ccb->peer_addr : NULL,
                              AVDT_DELAY_REPORT_CFM_EVT,
                              (tAVDT_CTRL *) &p_data->msg.hdr);
}

#if AVDT_REPORTING == TRUE
/*******************************************************************************
**
** Function         avdt_scb_hdl_tc_close_sto
**
** Description      This function is called when a channel is closed in OPEN
**                  state.  Check the channel type and process accordingly.
**
** Returns          Nothing.
**
*******************************************************************************/
void avdt_scb_hdl_tc_close_sto(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
{
    tAVDT_CTRL          avdt_ctrl;
    /* AVDT_CHAN_SIG does not visit this action */
    if(p_data && p_data->close.type != AVDT_CHAN_MEDIA)
    {
        /* it's reporting or recovery channel,
         * the channel close in open state means the peer does not support it */
        if(p_data->close.old_tc_state == AVDT_AD_ST_OPEN)
        {
            avdt_ctrl.hdr.err_code = 0;
            avdt_ctrl.hdr.err_param = 0;
            /* call app callback */
            (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb),
                                      p_scb->p_ccb ? p_scb->p_ccb->peer_addr : NULL,
                                      AVDT_REPORT_DISCONN_EVT, &avdt_ctrl);
        }
    }
    else
    {
        /* must be in OPEN state. need to go back to idle */
        avdt_scb_event(p_scb, AVDT_SCB_MSG_ABORT_RSP_EVT, NULL);
        avdt_scb_hdl_tc_close(p_scb, p_data);
    }
}
#endif

/*******************************************************************************
**
** Function         avdt_scb_hdl_tc_open
**
** Description      This function is called when the transport channel is
**                  opened while in the opening state.  It calls the
**                  application callback with an open indication or open
**                  confirm depending on who initiated the open procedure.
**
** Returns          Nothing.
**
*******************************************************************************/
void avdt_scb_hdl_tc_open(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
{
    UINT8   event;
#if AVDT_REPORTING == TRUE
    UINT8   role;
#endif

    /* stop transport channel connect timer */
    btu_stop_timer(&p_scb->timer_entry);

    event = (p_scb->role == AVDT_OPEN_INT) ? AVDT_OPEN_CFM_EVT : AVDT_OPEN_IND_EVT;
    p_data->open.hdr.err_code = 0;

    AVDT_TRACE_DEBUG3("psc_mask: cfg: 0x%x, req:0x%x, cur: 0x%x",
        p_scb->cs.cfg.psc_mask, p_scb->req_cfg.psc_mask, p_scb->curr_cfg.psc_mask);
#if AVDT_REPORTING == TRUE
    if(p_scb->curr_cfg.psc_mask & AVDT_PSC_REPORT)
    {
        /* open the reporting channel, if both devices support it */
        role = (p_scb->role == AVDT_OPEN_INT) ? AVDT_INT : AVDT_ACP;
        avdt_ad_open_req(AVDT_CHAN_REPORT, p_scb->p_ccb, p_scb, role);
    }
#endif

    /* call app callback */
    (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb),
                              p_scb->p_ccb ? p_scb->p_ccb->peer_addr : NULL,
                              event,
                              (tAVDT_CTRL *) &p_data->open);
}

#if AVDT_REPORTING == TRUE
/*******************************************************************************
**
** Function         avdt_scb_hdl_tc_open_sto
**
** Description      This function is called when the transport channel is
**                  opened while in the opening state.  It calls the
**                  application callback with an open indication or open
**                  confirm depending on who initiated the open procedure.
**
** Returns          Nothing.
**
*******************************************************************************/
void avdt_scb_hdl_tc_open_sto(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
{
    tAVDT_CTRL          avdt_ctrl;
    /* open reporting channel here, when it is implemented */

    /* call app callback */
    if(p_data->open.hdr.err_code == AVDT_CHAN_REPORT)
    {
        avdt_ctrl.hdr.err_code = 0;
        avdt_ctrl.hdr.err_param = 1;
        (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb),
                              p_scb->p_ccb ? p_scb->p_ccb->peer_addr : NULL,
                              AVDT_REPORT_CONN_EVT, &avdt_ctrl);
    }
}
#endif

/*******************************************************************************
**
** Function         avdt_scb_hdl_write_req_no_frag
**
** Description      This function frees the media packet currently stored in
**                  the SCB, if any.  Then it builds a new media packet from
**                  with the passed in buffer and stores it in the SCB.
**
** Returns          Nothing.
**
*******************************************************************************/
void avdt_scb_hdl_write_req_no_frag(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
{
    UINT8   *p;
    UINT32  ssrc;

    /* free packet we're holding, if any; to be replaced with new */
    if (p_scb->p_pkt != NULL)
    {
        GKI_freebuf(p_scb->p_pkt);

        /* this shouldn't be happening */
        AVDT_TRACE_WARNING0("Dropped media packet; congested");
    }

    /* build a media packet */
    /* Add RTP header if required */
    if ( !(p_data->apiwrite.opt & AVDT_DATA_OPT_NO_RTP) )
    {
        ssrc = avdt_scb_gen_ssrc(p_scb);

        p_data->apiwrite.p_buf->len += AVDT_MEDIA_HDR_SIZE;
        p_data->apiwrite.p_buf->offset -= AVDT_MEDIA_HDR_SIZE;

        p = (UINT8 *)(p_data->apiwrite.p_buf + 1) + p_data->apiwrite.p_buf->offset;

        UINT8_TO_BE_STREAM(p, AVDT_MEDIA_OCTET1);
        UINT8_TO_BE_STREAM(p, p_data->apiwrite.m_pt);
        UINT16_TO_BE_STREAM(p, p_scb->media_seq);
        UINT32_TO_BE_STREAM(p, p_data->apiwrite.time_stamp);
        UINT32_TO_BE_STREAM(p, ssrc);
    }

    p_scb->media_seq++;

    /* store it */
    p_scb->p_pkt = p_data->apiwrite.p_buf;
}

#if AVDT_MULTIPLEXING == TRUE
/*******************************************************************************
**
** Function         avdt_scb_hdl_write_req_frag
**
** Description      This function builds a new fragments of media packet from
**                  the passed in buffers and stores them in the SCB.
**
** Returns          Nothing.
**
*******************************************************************************/
void avdt_scb_hdl_write_req_frag(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
{
    UINT8   *p;
    UINT32  ssrc;
    BT_HDR  *p_frag;

    /* free fragments we're holding, if any; it shouldn't happen */
    if (!GKI_queue_is_empty(&p_scb->frag_q))
    {
        while((p_frag = (BT_HDR*)GKI_dequeue (&p_scb->frag_q)) != NULL)
            GKI_freebuf(p_frag);

        /* this shouldn't be happening */
        AVDT_TRACE_WARNING0("*** Dropped media packet; congested");
    }

    /* build a media fragments */
    p_scb->frag_off = p_data->apiwrite.data_len;
    p_scb->p_next_frag = p_data->apiwrite.p_data;

    ssrc = avdt_scb_gen_ssrc(p_scb);

    /* get first packet */
    p_frag = (BT_HDR*)GKI_getfirst (&p_data->apiwrite.frag_q);
    /* posit on Adaptation Layer header */
    p_frag->len += AVDT_AL_HDR_SIZE + AVDT_MEDIA_HDR_SIZE;
    p_frag->offset -= AVDT_AL_HDR_SIZE + AVDT_MEDIA_HDR_SIZE;
    p = (UINT8 *)(p_frag + 1) + p_frag->offset;

    /* Adaptation Layer header */
    /* TSID, no-fragment bit and coding of length(in 2 length octets following) */
    *p++ = (p_scb->curr_cfg.mux_tsid_media<<3) | AVDT_ALH_LCODE_16BIT;

    /* length of all remaining transport packet */
    UINT16_TO_BE_STREAM(p, p_frag->layer_specific+AVDT_MEDIA_HDR_SIZE );
    /* media header */
    UINT8_TO_BE_STREAM(p, AVDT_MEDIA_OCTET1);
    UINT8_TO_BE_STREAM(p, p_data->apiwrite.m_pt);
    UINT16_TO_BE_STREAM(p, p_scb->media_seq);
    UINT32_TO_BE_STREAM(p, p_data->apiwrite.time_stamp);
    UINT32_TO_BE_STREAM(p, ssrc);
    p_scb->media_seq++;

    while((p_frag = (BT_HDR*)GKI_getnext (p_frag)) != NULL)
    {
        /* posit on Adaptation Layer header */
        p_frag->len += AVDT_AL_HDR_SIZE;
        p_frag->offset -= AVDT_AL_HDR_SIZE;
        p = (UINT8 *)(p_frag + 1) + p_frag->offset;
        /* Adaptation Layer header */
        /* TSID, fragment bit and coding of length(in 2 length octets following) */
        *p++ = (p_scb->curr_cfg.mux_tsid_media<<3) | (AVDT_ALH_FRAG_MASK|AVDT_ALH_LCODE_16BIT);

        /* length of all remaining transport packet */
        UINT16_TO_BE_STREAM(p, p_frag->layer_specific );
    }

    /* store it */
    p_scb->frag_q = p_data->apiwrite.frag_q;
}
#endif


/*******************************************************************************
**
** Function         avdt_scb_hdl_write_req
**
** Description      This function calls one of the two versions of building functions
**                  for case with and without fragmentation
**
** Returns          Nothing.
**
*******************************************************************************/
void avdt_scb_hdl_write_req(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
{
#if AVDT_MULTIPLEXING == TRUE
    if (GKI_queue_is_empty(&p_data->apiwrite.frag_q))
#endif
        avdt_scb_hdl_write_req_no_frag(p_scb, p_data);
#if AVDT_MULTIPLEXING == TRUE
    else
        avdt_scb_hdl_write_req_frag(p_scb, p_data);
#endif
}

/*******************************************************************************
**
** Function         avdt_scb_snd_abort_req
**
** Description      This function sends an abort command message.
**
** Returns          Nothing.
**
*******************************************************************************/
void avdt_scb_snd_abort_req(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
{
    tAVDT_EVT_HDR   hdr;

    if (p_scb->p_ccb != NULL)
    {
        p_scb->role = AVDT_CLOSE_INT;

        hdr.seid = p_scb->peer_seid;

        avdt_msg_send_cmd(p_scb->p_ccb, p_scb, AVDT_SIG_ABORT, (tAVDT_MSG *) &hdr);
    }
}

/*******************************************************************************
**
** Function         avdt_scb_snd_abort_rsp
**
** Description      This function sends an abort response message.
**
** Returns          Nothing.
**
*******************************************************************************/
void avdt_scb_snd_abort_rsp(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
{
    avdt_msg_send_rsp(avdt_ccb_by_idx(p_data->msg.hdr.ccb_idx), AVDT_SIG_ABORT,
                      &p_data->msg);
}

/*******************************************************************************
**
** Function         avdt_scb_snd_close_req
**
** Description      This function sends a close command message.
**
** Returns          Nothing.
**
*******************************************************************************/
void avdt_scb_snd_close_req(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
{
    tAVDT_EVT_HDR   hdr;

    p_scb->role = AVDT_CLOSE_INT;

    hdr.seid = p_scb->peer_seid;

    avdt_msg_send_cmd(p_scb->p_ccb, p_scb, AVDT_SIG_CLOSE, (tAVDT_MSG *) &hdr);
}

/*******************************************************************************
**
** Function         avdt_scb_snd_stream_close
**
** Description      This function sends a close command message.
**
** Returns          Nothing.
**
*******************************************************************************/
void avdt_scb_snd_stream_close(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
{
#if AVDT_MULTIPLEXING == TRUE
    BT_HDR          *p_frag;

    AVDT_TRACE_WARNING2("avdt_scb_snd_stream_close c:%d, off:%d",
        p_scb->frag_q.count, p_scb->frag_off);
    /* clean fragments queue */
    while((p_frag = (BT_HDR*)GKI_dequeue (&p_scb->frag_q)) != NULL)
         GKI_freebuf(p_frag);
    p_scb->frag_off = 0;
#endif
    if (p_scb->p_pkt)
    {
        GKI_freebuf(p_scb->p_pkt);
        p_scb->p_pkt = NULL;
    }

#if 0
    if(p_scb->cong)
        p_scb->cong = FALSE;

    /* p_scb->curr_cfg.mux_tsid_media == 0 */
#endif
    avdt_scb_snd_close_req(p_scb, p_data);
}

/*******************************************************************************
**
** Function         avdt_scb_snd_close_rsp
**
** Description      This function sends a close response message.
**
** Returns          Nothing.
**
*******************************************************************************/
void avdt_scb_snd_close_rsp(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
{
    avdt_msg_send_rsp(p_scb->p_ccb, AVDT_SIG_CLOSE, &p_data->msg);
}

/*******************************************************************************
**
** Function         avdt_scb_snd_getconfig_req
**
** Description      This function sends a get configuration command message.
**
** Returns          Nothing.
**
*******************************************************************************/
void avdt_scb_snd_getconfig_req(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
{
    tAVDT_EVT_HDR   hdr;

    hdr.seid = p_scb->peer_seid;

    avdt_msg_send_cmd(p_scb->p_ccb, p_scb, AVDT_SIG_GETCONFIG, (tAVDT_MSG *) &hdr);
}

/*******************************************************************************
**
** Function         avdt_scb_snd_getconfig_rsp
**
** Description      This function sends a get configuration response message.
**
** Returns          Nothing.
**
*******************************************************************************/
void avdt_scb_snd_getconfig_rsp(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
{
    avdt_msg_send_rsp(p_scb->p_ccb, AVDT_SIG_GETCONFIG, &p_data->msg);
}

/*******************************************************************************
**
** Function         avdt_scb_snd_open_req
**
** Description      This function sends an open command message.
**
** Returns          Nothing.
**
*******************************************************************************/
void avdt_scb_snd_open_req(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
{
    tAVDT_EVT_HDR   hdr;

    hdr.seid = p_scb->peer_seid;

    avdt_msg_send_cmd(p_scb->p_ccb, p_scb, AVDT_SIG_OPEN, (tAVDT_MSG *) &hdr);
}

/*******************************************************************************
**
** Function         avdt_scb_snd_open_rsp
**
** Description      This function sends an open response message.  It also
**                  calls avdt_ad_open_req() to accept a transport channel
**                  connection.
**
** Returns          Nothing.
**
*******************************************************************************/
void avdt_scb_snd_open_rsp(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
{
    /* notify adaption that we're waiting for transport channel open */
    p_scb->role = AVDT_OPEN_ACP;
    avdt_ad_open_req(AVDT_CHAN_MEDIA, p_scb->p_ccb, p_scb, AVDT_ACP);

    /* send response */
    avdt_msg_send_rsp(p_scb->p_ccb, AVDT_SIG_OPEN, &p_data->msg);

    /* start tc connect timer */
    btu_start_timer(&p_scb->timer_entry, BTU_TTYPE_AVDT_SCB_TC, AVDT_SCB_TC_CONN_TOUT);
}

/*******************************************************************************
**
** Function         avdt_scb_snd_reconfig_req
**
** Description      This function stores the configuration parameters in the
**                  SCB and sends a reconfiguration command message.
**
** Returns          Nothing.
**
*******************************************************************************/
void avdt_scb_snd_reconfig_req(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
{
    memcpy(&p_scb->req_cfg, p_data->msg.config_cmd.p_cfg, sizeof(tAVDT_CFG));
    p_data->msg.hdr.seid = p_scb->peer_seid;
    avdt_msg_send_cmd(p_scb->p_ccb, p_scb, AVDT_SIG_RECONFIG, &p_data->msg);
}

/*******************************************************************************
**
** Function         avdt_scb_snd_reconfig_rsp
**
** Description      This function stores the configuration parameters in the
**                  SCB and sends a reconfiguration response message.
**
** Returns          Nothing.
**
*******************************************************************************/
void avdt_scb_snd_reconfig_rsp(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
{
    if (p_data->msg.hdr.err_code == 0)
    {
        /* store new configuration */
        if (p_scb->req_cfg.num_codec > 0)
        {
            p_scb->curr_cfg.num_codec = p_scb->req_cfg.num_codec;
            memcpy(p_scb->curr_cfg.codec_info, p_scb->req_cfg.codec_info, AVDT_CODEC_SIZE);
        }
        if (p_scb->req_cfg.num_protect > 0)
        {
            p_scb->curr_cfg.num_protect = p_scb->req_cfg.num_protect;
            memcpy(p_scb->curr_cfg.protect_info, p_scb->req_cfg.protect_info, AVDT_PROTECT_SIZE);
        }

        /* send response */
        avdt_msg_send_rsp(p_scb->p_ccb, AVDT_SIG_RECONFIG, &p_data->msg);
    }
    else
    {
        /* send reject */
        avdt_msg_send_rej(p_scb->p_ccb, AVDT_SIG_RECONFIG, &p_data->msg);
    }
}

/*******************************************************************************
**
** Function         avdt_scb_snd_security_req
**
** Description      This function sends a security command message.
**
** Returns          Nothing.
**
*******************************************************************************/
void avdt_scb_snd_security_req(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
{
    p_data->msg.hdr.seid = p_scb->peer_seid;
    avdt_msg_send_cmd(p_scb->p_ccb, p_scb, AVDT_SIG_SECURITY, &p_data->msg);
}

/*******************************************************************************
**
** Function         avdt_scb_snd_security_rsp
**
** Description      This function sends a security response message.
**
** Returns          Nothing.
**
*******************************************************************************/
void avdt_scb_snd_security_rsp(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
{
    if (p_data->msg.hdr.err_code == 0)
    {
        avdt_msg_send_rsp(p_scb->p_ccb, AVDT_SIG_SECURITY, &p_data->msg);
    }
    else
    {
        avdt_msg_send_rej(p_scb->p_ccb, AVDT_SIG_SECURITY, &p_data->msg);
    }
}

/*******************************************************************************
**
** Function         avdt_scb_snd_setconfig_rej
**
** Description      This function marks the SCB as not in use and sends a
**                  set configuration reject message.
**
** Returns          Nothing.
**
*******************************************************************************/
void avdt_scb_snd_setconfig_rej(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
{
    if (p_scb->p_ccb != NULL)
    {
        avdt_msg_send_rej(p_scb->p_ccb, AVDT_SIG_SETCONFIG, &p_data->msg);

        /* clear scb variables */
        avdt_scb_clr_vars(p_scb, p_data);
    }
}

/*******************************************************************************
**
** Function         avdt_scb_snd_setconfig_req
**
** Description      This function marks the SCB as in use and copies the
**                  configuration parameters to the SCB.  Then the function
**                  sends a set configuration command message and initiates
**                  opening of the signaling channel.
**
** Returns          Nothing.
**
*******************************************************************************/
void avdt_scb_snd_setconfig_req(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
{
    tAVDT_CFG *p_req, *p_cfg;

    /* copy API parameters to scb, set scb as in use */
    p_scb->in_use = TRUE;
    p_scb->p_ccb = avdt_ccb_by_idx(p_data->msg.config_cmd.hdr.ccb_idx);
    p_scb->peer_seid = p_data->msg.config_cmd.hdr.seid;
    p_req = p_data->msg.config_cmd.p_cfg;
    p_cfg = &p_scb->cs.cfg;
#if AVDT_MULTIPLEXING == TRUE
    p_req->mux_tsid_media = p_cfg->mux_tsid_media;
    p_req->mux_tcid_media = p_cfg->mux_tcid_media;
    if(p_req->psc_mask & AVDT_PSC_REPORT)
    {
        p_req->mux_tsid_report = p_cfg->mux_tsid_report;
        p_req->mux_tcid_report = p_cfg->mux_tcid_report;
    }
#endif
    memcpy(&p_scb->req_cfg, p_data->msg.config_cmd.p_cfg, sizeof(tAVDT_CFG));

    avdt_msg_send_cmd(p_scb->p_ccb, p_scb, AVDT_SIG_SETCONFIG, &p_data->msg);

    /* tell ccb to open channel */
    avdt_ccb_event(p_scb->p_ccb, AVDT_CCB_UL_OPEN_EVT, NULL);
}

/*******************************************************************************
**
** Function         avdt_scb_snd_setconfig_rsp
**
** Description      This function copies the requested configuration into the
**                  current configuration and sends a set configuration
**                  response message.
**
** Returns          Nothing.
**
*******************************************************************************/
void avdt_scb_snd_setconfig_rsp(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
{
    if (p_scb->p_ccb != NULL)
    {
        memcpy(&p_scb->curr_cfg, &p_scb->req_cfg, sizeof(tAVDT_CFG));

        avdt_msg_send_rsp(p_scb->p_ccb, AVDT_SIG_SETCONFIG, &p_data->msg);
    }
}

/*******************************************************************************
**
** Function         avdt_scb_snd_tc_close
**
** Description      This function calls avdt_ad_close_req() to close the
**                  transport channel for this SCB.
**
** Returns          Nothing.
**
*******************************************************************************/
void avdt_scb_snd_tc_close(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
{
#if AVDT_REPORTING == TRUE
    if(p_scb->curr_cfg.psc_mask & AVDT_PSC_REPORT)
        avdt_ad_close_req(AVDT_CHAN_REPORT, p_scb->p_ccb, p_scb);
#endif
    avdt_ad_close_req(AVDT_CHAN_MEDIA, p_scb->p_ccb, p_scb);
}

/*******************************************************************************
**
** Function         avdt_scb_cb_err
**
** Description      This function calls the application callback function
**                  indicating an error.
**
** Returns          Nothing.
**
*******************************************************************************/
void avdt_scb_cb_err(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
{
    tAVDT_CTRL          avdt_ctrl;

    /* set error code and parameter */
    avdt_ctrl.hdr.err_code = AVDT_ERR_BAD_STATE;
    avdt_ctrl.hdr.err_param = 0;

    /* call callback, using lookup table to get callback event */
    (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb),
                              NULL,
                              avdt_scb_cback_evt[p_scb->curr_evt],
                              &avdt_ctrl);
}

/*******************************************************************************
**
** Function         avdt_scb_cong_state
**
** Description      This function sets the congestion state of the SCB media
**                  transport channel.
**
** Returns          Nothing.
**
*******************************************************************************/
void avdt_scb_cong_state(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
{
    p_scb->cong = p_data->llcong;
}

/*******************************************************************************
**
** Function         avdt_scb_rej_state
**
** Description      This function sends a reject message to the peer indicating
**                  incorrect state for the received command message.
**
** Returns          Nothing.
**
*******************************************************************************/
void avdt_scb_rej_state(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
{
    p_data->msg.hdr.err_code = AVDT_ERR_BAD_STATE;
    p_data->msg.hdr.err_param = 0;
    avdt_msg_send_rej(avdt_ccb_by_idx(p_data->msg.hdr.ccb_idx),
                      p_data->msg.hdr.sig_id, &p_data->msg);
}

/*******************************************************************************
**
** Function         avdt_scb_rej_in_use
**
** Description      This function sends a reject message to the peer indicating
**                  the stream is in use.
**
** Returns          Nothing.
**
*******************************************************************************/
void avdt_scb_rej_in_use(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
{
    p_data->msg.hdr.err_code = AVDT_ERR_IN_USE;
    p_data->msg.hdr.err_param = 0;
    avdt_msg_send_rej(avdt_ccb_by_idx(p_data->msg.hdr.ccb_idx),
                      p_data->msg.hdr.sig_id, &p_data->msg);
}

/*******************************************************************************
**
** Function         avdt_scb_rej_not_in_use
**
** Description      This function sends a reject message to the peer indicating
**                  the stream is in use.
**
** Returns          Nothing.
**
*******************************************************************************/
void avdt_scb_rej_not_in_use(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
{
    p_data->msg.hdr.err_code = AVDT_ERR_NOT_IN_USE;
    p_data->msg.hdr.err_param = 0;
    avdt_msg_send_rej(avdt_ccb_by_idx(p_data->msg.hdr.ccb_idx),
                      p_data->msg.hdr.sig_id, &p_data->msg);
}

/*******************************************************************************
**
** Function         avdt_scb_set_remove
**
** Description      This function marks an SCB to be removed.
**
** Returns          Nothing.
**
*******************************************************************************/
void avdt_scb_set_remove(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
{
    p_scb->remove = TRUE;
}

/*******************************************************************************
**
** Function         avdt_scb_free_pkt
**
** Description      This function frees the media packet passed in.
**
** Returns          Nothing.
**
*******************************************************************************/
void avdt_scb_free_pkt(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
{
    tAVDT_CTRL      avdt_ctrl;
#if AVDT_MULTIPLEXING == TRUE
    BT_HDR          *p_frag;
#endif

    /* set error code and parameter */
    avdt_ctrl.hdr.err_code = AVDT_ERR_BAD_STATE;
    avdt_ctrl.hdr.err_param = 0;

    /* p_buf can be NULL in case using of fragments queue frag_q */
    if(p_data->apiwrite.p_buf)
        GKI_freebuf(p_data->apiwrite.p_buf);

#if AVDT_MULTIPLEXING == TRUE
    /* clean fragments queue */
    while((p_frag = (BT_HDR*)GKI_dequeue (&p_data->apiwrite.frag_q)) != NULL)
         GKI_freebuf(p_frag);
#endif

    AVDT_TRACE_WARNING0("Dropped media packet");

    /* we need to call callback to keep data flow going */
    (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb), NULL, AVDT_WRITE_CFM_EVT,
                              &avdt_ctrl);
}

/*******************************************************************************
**
** Function         avdt_scb_clr_pkt
**
** Description      This function frees the media packet stored in the SCB.
**
** Returns          Nothing.
**
*******************************************************************************/
void avdt_scb_clr_pkt(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
{
    tAVDT_CTRL      avdt_ctrl;
    tAVDT_CCB       *p_ccb;
    UINT8           tcid;
    UINT16          lcid;
#if AVDT_MULTIPLEXING == TRUE
    BT_HDR          *p_frag;
#endif

    /* set error code and parameter */
    avdt_ctrl.hdr.err_code = AVDT_ERR_BAD_STATE;
    avdt_ctrl.hdr.err_param = 0;
    /* flush the media data queued at L2CAP */
    if((p_ccb = p_scb->p_ccb) != NULL)
    {
        /* get tcid from type, scb */
        tcid = avdt_ad_type_to_tcid(AVDT_CHAN_MEDIA, p_scb);

        lcid = avdt_cb.ad.rt_tbl[avdt_ccb_to_idx(p_ccb)][tcid].lcid;
        L2CA_FlushChannel (lcid, L2CAP_FLUSH_CHANS_ALL);
    }

    if (p_scb->p_pkt != NULL)
    {
        GKI_freebuf(p_scb->p_pkt);
        p_scb->p_pkt = NULL;

        AVDT_TRACE_DEBUG0("Dropped stored media packet");

        /* we need to call callback to keep data flow going */
        (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb), NULL, AVDT_WRITE_CFM_EVT,
                                  &avdt_ctrl);
    }
#if AVDT_MULTIPLEXING == TRUE
    else if(!GKI_queue_is_empty (&p_scb->frag_q))
    {
        AVDT_TRACE_DEBUG0("Dropped fragments queue");
        /* clean fragments queue */
        while((p_frag = (BT_HDR*)GKI_dequeue (&p_scb->frag_q)) != NULL)
             GKI_freebuf(p_frag);

        p_scb->frag_off = 0;

        /* we need to call callback to keep data flow going */
        (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb), NULL, AVDT_WRITE_CFM_EVT,
                                  &avdt_ctrl);
    }
#endif
}


/*******************************************************************************
**
** Function         avdt_scb_chk_snd_pkt
**
** Description      This function checks if the SCB is congested, and if not
**                  congested it sends a stored media packet, if any.  After it
**                  sends the packet it calls the application callback function
**                  with a write confirm.
**
** Returns          Nothing.
**
*******************************************************************************/
void avdt_scb_chk_snd_pkt(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
{
    tAVDT_CTRL      avdt_ctrl;
    BT_HDR          *p_pkt;
#if AVDT_MULTIPLEXING == TRUE
    BOOLEAN         sent = FALSE;
    UINT8   res = AVDT_AD_SUCCESS;
    tAVDT_SCB_EVT data;
#endif

    avdt_ctrl.hdr.err_code = 0;

    if (!p_scb->cong)
    {
        if (p_scb->p_pkt != NULL)
        {
            p_pkt = p_scb->p_pkt;
            p_scb->p_pkt = NULL;
            avdt_ad_write_req(AVDT_CHAN_MEDIA, p_scb->p_ccb, p_scb, p_pkt);

            (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb), NULL, AVDT_WRITE_CFM_EVT, &avdt_ctrl);
        }
#if AVDT_MULTIPLEXING == TRUE
        else
        {
#if 0
            AVDT_TRACE_DEBUG1("num_q=%d",
                L2CA_FlushChannel(avdt_cb.ad.rt_tbl[avdt_ccb_to_idx(p_scb->p_ccb)][avdt_ad_type_to_tcid(AVDT_CHAN_MEDIA, p_scb)].lcid),
                                  L2CAP_FLUSH_CHANS_GET);
#endif
            while((p_pkt = (BT_HDR*)GKI_dequeue (&p_scb->frag_q)) != NULL)
            {
                sent = TRUE;
                AVDT_TRACE_DEBUG1("Send fragment len=%d",p_pkt->len);
                /* fragments queue contains fragment to send */
                res = avdt_ad_write_req(AVDT_CHAN_MEDIA, p_scb->p_ccb, p_scb, p_pkt);
                if(AVDT_AD_CONGESTED == res)
                {
                    p_scb->cong = TRUE;
                    AVDT_TRACE_DEBUG0("avdt/l2c congested!!");
                    break;/* exit loop if channel became congested */
            }
            }
            AVDT_TRACE_DEBUG2("res=%d left=%d",res, p_scb->frag_off);

            if(p_scb->frag_off)
            {
                if(AVDT_AD_SUCCESS == res || GKI_queue_is_empty (&p_scb->frag_q))
                {
                    /* all buffers were sent to L2CAP, compose more to queue */
                    avdt_scb_queue_frags(p_scb, &p_scb->p_next_frag, &p_scb->frag_off, &p_scb->frag_q);
                    if(!GKI_queue_is_empty (&p_scb->frag_q))
                    {
                        data.llcong = p_scb->cong;
                        avdt_scb_event(p_scb, AVDT_SCB_TC_CONG_EVT, &data);
                    }
                }
            }

            /* Send event AVDT_WRITE_CFM_EVT if it was last fragment */
            else if (sent && GKI_queue_is_empty (&p_scb->frag_q))
            {
                (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb), NULL, AVDT_WRITE_CFM_EVT, &avdt_ctrl);
            }
        }
#endif
    }
}

/*******************************************************************************
**
** Function         avdt_scb_tc_timer
**
** Description      This function is called to start a timer when the peer
**                  initiates closing of the stream.  The timer verifies that
**                  the peer disconnects the transport channel.
**
** Returns          Nothing.
**
*******************************************************************************/
void avdt_scb_tc_timer(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
{
    btu_start_timer(&p_scb->timer_entry, BTU_TTYPE_AVDT_SCB_TC, AVDT_SCB_TC_DISC_TOUT);
}

/*******************************************************************************
**
** Function         avdt_scb_clr_vars
**
** Description      This function initializes certain SCB variables.
**
** Returns          Nothing.
**
*******************************************************************************/
void avdt_scb_clr_vars(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
{
    p_scb->in_use = FALSE;
    p_scb->p_ccb = NULL;
    p_scb->peer_seid = 0;
}

#if AVDT_MULTIPLEXING == TRUE
/*******************************************************************************
**
** Function         avdt_scb_queue_frags
**
** Description      This function breaks media payload into fragments
**                  and put the fragments in the given queue.
**
** Returns          Nothing.
**
*******************************************************************************/
void avdt_scb_queue_frags(tAVDT_SCB *p_scb, UINT8 **pp_data, UINT32 *p_data_len, BUFFER_Q *pq)
{
    UINT16  lcid;
    UINT16  num_frag;
    UINT16  mtu_used;
    UINT8   *p;
    BOOLEAN al_hdr = FALSE;
    UINT8   tcid;
    tAVDT_TC_TBL    *p_tbl;
    UINT16          buf_size;
    UINT16          offset = AVDT_MEDIA_OFFSET + AVDT_AL_HDR_SIZE;
    UINT16          cont_offset = offset - AVDT_MEDIA_HDR_SIZE;
    BT_HDR          *p_frag;

    tcid = avdt_ad_type_to_tcid(AVDT_CHAN_MEDIA, p_scb);
    lcid = avdt_cb.ad.rt_tbl[avdt_ccb_to_idx(p_scb->p_ccb)][tcid].lcid;

    if( p_scb->frag_off != 0)
    {
        /* continuing process is usually triggered by un-congest event.
         * the number of buffers at L2CAP is very small (if not 0).
         * we do not need to L2CA_FlushChannel() */
        offset = cont_offset;
        al_hdr = TRUE;
        num_frag = AVDT_MAX_FRAG_COUNT;
    }
    else
    {
        num_frag = L2CA_FlushChannel(lcid, L2CAP_FLUSH_CHANS_GET);
        AVDT_TRACE_DEBUG2("num_q=%d lcid=%d", num_frag, lcid);
        if(num_frag >= AVDT_MAX_FRAG_COUNT)
        {
            num_frag = 0;
        }
        else
        {
            num_frag = AVDT_MAX_FRAG_COUNT - num_frag;
        }
    }

    /* look up transport channel table entry to get peer mtu */
    p_tbl = avdt_ad_tc_tbl_by_type(AVDT_CHAN_MEDIA, p_scb->p_ccb, p_scb);
    buf_size = p_tbl->peer_mtu + BT_HDR_SIZE;
    AVDT_TRACE_DEBUG3("peer_mtu: %d, buf_size: %d num_frag=%d",
        p_tbl->peer_mtu, buf_size, num_frag);

    if(buf_size > AVDT_DATA_POOL_SIZE)
        buf_size = AVDT_DATA_POOL_SIZE;

    mtu_used = buf_size - BT_HDR_SIZE;

    while(*p_data_len && num_frag)
    {
        /* allocate buffer for fragment */
        if(NULL == (p_frag = (BT_HDR*)GKI_getbuf(buf_size)))
        {
            AVDT_TRACE_WARNING1("avdt_scb_queue_frags len=%d(out of GKI buffers)",*p_data_len);
            break;
        }
        /* fill fragment by chunk of media payload */
        p_frag->layer_specific = *p_data_len;/* length of all remaining transport packet */
        p_frag->offset = offset;
        /* adjust packet offset for continuing packets */
        offset = cont_offset;

        p_frag->len = mtu_used - p_frag->offset;
        if(p_frag->len > *p_data_len)
            p_frag->len = *p_data_len;
        memcpy((UINT8*)(p_frag+1) + p_frag->offset, *pp_data, p_frag->len);
        *pp_data += p_frag->len;
        *p_data_len -= p_frag->len;
        AVDT_TRACE_DEBUG1("Prepared fragment len=%d", p_frag->len);

        if(al_hdr)
        {
            /* Adaptation Layer header */
            p_frag->len += AVDT_AL_HDR_SIZE;
            p_frag->offset -= AVDT_AL_HDR_SIZE;
            p = (UINT8 *)(p_frag + 1) + p_frag->offset;
            /* TSID, fragment bit and coding of length(in 2 length octets following) */
            *p++ = (p_scb->curr_cfg.mux_tsid_media<<3) | (AVDT_ALH_FRAG_MASK|AVDT_ALH_LCODE_16BIT);

            /* length of all remaining transport packet */
            UINT16_TO_BE_STREAM(p, p_frag->layer_specific );
        }
        /* put fragment into gueue */
        GKI_enqueue(pq, p_frag);
        num_frag--;
    }
}
#endif

