普通文本  |  608行  |  20.34 KB

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

/******************************************************************************
 *
 *  This module contains the AVDTP adaption layer.
 *
 ******************************************************************************/

#include <base/logging.h>
#include <string.h>

#include "avdt_api.h"
#include "avdt_int.h"
#include "avdtc_api.h"
#include "bt_target.h"
#include "bt_types.h"
#include "bt_utils.h"
#include "l2c_api.h"
#include "l2cdefs.h"
#include "osi/include/osi.h"

AvdtpScb* AvdtpAdaptationLayer::LookupAvdtpScb(
    const AvdtpTransportChannel& tc) {
  if (tc.ccb_idx >= AVDT_NUM_LINKS) {
    AVDT_TRACE_ERROR("%s: AvdtpScb entry not found: invalid ccb_idx:%d",
                     __func__, tc.ccb_idx);
    return nullptr;
  }
  if (tc.tcid >= AVDT_NUM_RT_TBL) {
    AVDT_TRACE_ERROR("%s: AvdtpScb entry not found: invalid tcid:%d", __func__,
                     tc.tcid);
    return nullptr;
  }
  const AvdtpRoutingEntry& re = rt_tbl[tc.ccb_idx][tc.tcid];
  AVDT_TRACE_DEBUG("%s: ccb_idx:%d tcid:%d scb_hdl:%d", __func__, tc.ccb_idx,
                   tc.tcid, re.scb_hdl);
  return avdt_scb_by_hdl(re.scb_hdl);
}

/*******************************************************************************
 *
 * Function         avdt_ad_type_to_tcid
 *
 * Description      Derives the TCID from the channel type and SCB.
 *
 *
 * Returns          TCID value.
 *
 ******************************************************************************/
uint8_t avdt_ad_type_to_tcid(uint8_t type, AvdtpScb* p_scb) {
  if (type == AVDT_CHAN_SIG) {
    return 0;
  }
  // The SCB Handle is unique in the [1, AVDT_NUM_LINKS * AVDT_NUM_SEPS]
  // range. The scb_idx computed here is the SCB index for the corresponding
  // SEP, and it is in the range [0, AVDT_NUM_SEPS) for a particular link.
  uint8_t scb_idx = (avdt_scb_to_hdl(p_scb) - 1) % AVDT_NUM_LINKS;
  // There are AVDT_CHAN_NUM_TYPES channel types per SEP. Here we compute
  // the type index (TCID) from the SEP index and the type itself.
  uint8_t tcid = (scb_idx * (AVDT_CHAN_NUM_TYPES - 1)) + type;
  AVDT_TRACE_DEBUG("%s: type:%d, tcid: %d", __func__, type, tcid);
  return tcid;
}

/*******************************************************************************
 *
 * Function         avdt_ad_tcid_to_type
 *
 * Description      Derives the channel type from the TCID.
 *
 *
 * Returns          Channel type value.
 *
 ******************************************************************************/
static uint8_t avdt_ad_tcid_to_type(uint8_t tcid) {
  uint8_t type;

  if (tcid == 0) {
    type = AVDT_CHAN_SIG;
  } else {
    /* tcid translates to type based on number of channels, as follows:
    ** only media channel   :  tcid=1,2,3,4,5,6...  type=1,1,1,1,1,1...
    ** media and report     :  tcid=1,2,3,4,5,6...  type=1,2,1,2,1,2...
    ** media, report, recov :  tcid=1,2,3,4,5,6...  type=1,2,3,1,2,3...
    */
    type = ((tcid + AVDT_CHAN_NUM_TYPES - 2) % (AVDT_CHAN_NUM_TYPES - 1)) + 1;
  }
  AVDT_TRACE_DEBUG("tcid: %d, type: %d", tcid, type);
  return type;
}

/*******************************************************************************
 *
 * Function         avdt_ad_init
 *
 * Description      Initialize adaption layer.
 *
 *
 * Returns          Nothing.
 *
 ******************************************************************************/
void avdt_ad_init(void) {
  int i;
  AvdtpTransportChannel* p_tbl = avdtp_cb.ad.tc_tbl;
  avdtp_cb.ad.Reset();

  /* make sure the peer_mtu is a valid value */
  for (i = 0; i < AVDT_NUM_TC_TBL; i++, p_tbl++) {
    p_tbl->peer_mtu = L2CAP_DEFAULT_MTU;
  }
}

/*******************************************************************************
 *
 * Function         avdt_ad_tc_tbl_by_st
 *
 * Description      Find adaption layer transport channel table entry matching
 *                  the given state.
 *
 *
 * Returns          Pointer to matching entry.  For control channel it returns
 *                  the matching entry.  For media or other it returns the
 *                  first matching entry (there could be more than one).
 *
 ******************************************************************************/
AvdtpTransportChannel* avdt_ad_tc_tbl_by_st(uint8_t type, AvdtpCcb* p_ccb,
                                            uint8_t state) {
  int i;
  AvdtpTransportChannel* p_tbl = avdtp_cb.ad.tc_tbl;
  uint8_t ccb_idx;

  if (p_ccb == NULL) {
    /* resending security req */
    for (i = 0; i < AVDT_NUM_TC_TBL; i++, p_tbl++) {
      /* must be AVDT_CHAN_SIG - tcid always zero */
      if ((p_tbl->tcid == 0) && (p_tbl->state == state)) {
        break;
      }
    }
  } else {
    ccb_idx = avdt_ccb_to_idx(p_ccb);

    for (i = 0; i < AVDT_NUM_TC_TBL; i++, p_tbl++) {
      if (type == AVDT_CHAN_SIG) {
        /* if control channel, tcid always zero */
        if ((p_tbl->tcid == 0) && (p_tbl->ccb_idx == ccb_idx) &&
            (p_tbl->state == state)) {
          break;
        }
      } else {
        /* if other channel, tcid is always > zero */
        if ((p_tbl->tcid > 0) && (p_tbl->ccb_idx == ccb_idx) &&
            (p_tbl->state == state)) {
          break;
        }
      }
    }
  }

  /* if nothing found return null */
  if (i == AVDT_NUM_TC_TBL) {
    p_tbl = NULL;
  }

  return p_tbl;
}

/*******************************************************************************
 *
 * Function         avdt_ad_tc_tbl_by_lcid
 *
 * Description      Find adaption layer transport channel table entry by LCID.
 *
 *
 * Returns          Pointer to entry.
 *
 ******************************************************************************/
AvdtpTransportChannel* avdt_ad_tc_tbl_by_lcid(uint16_t lcid) {
  uint8_t idx;

  idx = avdtp_cb.ad.lcid_tbl[lcid - L2CAP_BASE_APPL_CID];

  if (idx < AVDT_NUM_TC_TBL) {
    return &avdtp_cb.ad.tc_tbl[idx];
  } else {
    return NULL;
  }
}

/*******************************************************************************
 *
 * Function         avdt_ad_tc_tbl_by_type
 *
 * Description      This function retrieves the transport channel table entry
 *                  for a particular channel.
 *
 *
 * Returns          Pointer to transport channel table entry.
 *
 ******************************************************************************/
AvdtpTransportChannel* avdt_ad_tc_tbl_by_type(uint8_t type, AvdtpCcb* p_ccb,
                                              AvdtpScb* p_scb) {
  uint8_t tcid;
  int i;
  AvdtpTransportChannel* p_tbl = avdtp_cb.ad.tc_tbl;
  uint8_t ccb_idx = avdt_ccb_to_idx(p_ccb);

  /* get tcid from type, scb */
  tcid = avdt_ad_type_to_tcid(type, p_scb);

  for (i = 0; i < AVDT_NUM_TC_TBL; i++, p_tbl++) {
    if ((p_tbl->tcid == tcid) && (p_tbl->ccb_idx == ccb_idx)) {
      break;
    }
  }

  CHECK(i != AVDT_NUM_TC_TBL);

  return p_tbl;
}

/*******************************************************************************
 *
 * Function         avdt_ad_tc_tbl_alloc
 *
 * Description      Allocate an entry in the traffic channel table.
 *
 *
 * Returns          Pointer to entry.
 *
 ******************************************************************************/
AvdtpTransportChannel* avdt_ad_tc_tbl_alloc(AvdtpCcb* p_ccb) {
  int i;
  AvdtpTransportChannel* p_tbl = avdtp_cb.ad.tc_tbl;

  /* find next free entry in tc table */
  for (i = 0; i < AVDT_NUM_TC_TBL; i++, p_tbl++) {
    if (p_tbl->state == AVDT_AD_ST_UNUSED) {
      break;
    }
  }

  /* sanity check */
  CHECK(i != AVDT_NUM_TC_TBL);

  /* initialize entry */
  p_tbl->peer_mtu = L2CAP_DEFAULT_MTU;
  p_tbl->cfg_flags = 0;
  p_tbl->ccb_idx = avdt_ccb_to_idx(p_ccb);
  p_tbl->state = AVDT_AD_ST_IDLE;
  return p_tbl;
}

/*******************************************************************************
 *
 * Function         avdt_ad_tc_tbl_to_idx
 *
 * Description      Convert a transport channel table entry to an index.
 *
 *
 * Returns          Index value.
 *
 ******************************************************************************/
uint8_t avdt_ad_tc_tbl_to_idx(AvdtpTransportChannel* p_tbl) {
  AVDT_TRACE_DEBUG("avdt_ad_tc_tbl_to_idx: %d", (p_tbl - avdtp_cb.ad.tc_tbl));
  /* use array arithmetic to determine index */
  return (uint8_t)(p_tbl - avdtp_cb.ad.tc_tbl);
}

/*******************************************************************************
 *
 * Function         avdt_ad_tc_close_ind
 *
 * Description      This function is called by the L2CAP interface when the
 *                  L2CAP channel is closed.  It looks up the CCB or SCB for
 *                  the channel and sends it a close event.  The reason
 *                  parameter is the same value passed by the L2CAP
 *                  callback function.
 *
 *
 * Returns          Nothing.
 *
 ******************************************************************************/
void avdt_ad_tc_close_ind(AvdtpTransportChannel* p_tbl,
                          UNUSED_ATTR uint16_t reason) {
  AvdtpCcb* p_ccb;
  AvdtpScb* p_scb;
  tAVDT_SCB_TC_CLOSE close;

  close.old_tc_state = p_tbl->state;
  /* clear avdt_ad_tc_tbl entry */
  p_tbl->state = AVDT_AD_ST_UNUSED;
  p_tbl->cfg_flags = 0;
  p_tbl->peer_mtu = L2CAP_DEFAULT_MTU;

  AVDT_TRACE_DEBUG("%s: tcid: %d, old: %d", __func__, p_tbl->tcid,
                   close.old_tc_state);
  /* if signaling channel, notify ccb that channel open */
  if (p_tbl->tcid == 0) {
    p_ccb = avdt_ccb_by_idx(p_tbl->ccb_idx);
    avdt_ccb_event(p_ccb, AVDT_CCB_LL_CLOSE_EVT, NULL);
    return;
  }
  /* if media or other channel, notify scb that channel close */
  /* look up scb in stream routing table by ccb, tcid */
  p_scb = avdtp_cb.ad.LookupAvdtpScb(*p_tbl);
  if (p_scb == nullptr) {
    AVDT_TRACE_ERROR("%s: Cannot find AvdtScb entry: ccb_idx:%d tcid:%d",
                     __func__, p_tbl->ccb_idx, p_tbl->tcid);
    return;
  }
  close.tcid = p_tbl->tcid;
  close.type = avdt_ad_tcid_to_type(p_tbl->tcid);
  tAVDT_SCB_EVT avdt_scb_evt;
  avdt_scb_evt.close = close;
  avdt_scb_event(p_scb, AVDT_SCB_TC_CLOSE_EVT, &avdt_scb_evt);
}

/*******************************************************************************
 *
 * Function         avdt_ad_tc_open_ind
 *
 * Description      This function is called by the L2CAP interface when
 *                  the L2CAP channel is opened.  It looks up the CCB or SCB
 *                  for the channel and sends it an open event.
 *
 *
 * Returns          Nothing.
 *
 ******************************************************************************/
void avdt_ad_tc_open_ind(AvdtpTransportChannel* p_tbl) {
  AvdtpCcb* p_ccb;
  AvdtpScb* p_scb;
  tAVDT_OPEN open;
  tAVDT_EVT_HDR evt;

  AVDT_TRACE_DEBUG("%s: p_tbl:%p state:%d ccb_idx:%d tcid:%d scb_hdl:%d",
                   __func__, p_tbl, p_tbl->state, p_tbl->ccb_idx, p_tbl->tcid,
                   avdtp_cb.ad.rt_tbl[p_tbl->ccb_idx][p_tbl->tcid].scb_hdl);

  p_tbl->state = AVDT_AD_ST_OPEN;

  /* if signaling channel, notify ccb that channel open */
  if (p_tbl->tcid == 0) {
    /* set the signal channel to use high priority within the ACL link */
    L2CA_SetTxPriority(avdtp_cb.ad.rt_tbl[p_tbl->ccb_idx][AVDT_CHAN_SIG].lcid,
                       L2CAP_CHNL_PRIORITY_HIGH);

    p_ccb = avdt_ccb_by_idx(p_tbl->ccb_idx);
    /* use err_param to indicate the role of connection.
     * AVDT_ACP, if ACP */
    evt.err_param = AVDT_INT;
    if (p_tbl->cfg_flags & AVDT_L2C_CFG_CONN_ACP) {
      evt.err_param = AVDT_ACP;
    }
    tAVDT_CCB_EVT avdt_ccb_evt;
    avdt_ccb_evt.msg.hdr = evt;
    avdt_ccb_event(p_ccb, AVDT_CCB_LL_OPEN_EVT, &avdt_ccb_evt);
    return;
  }
  /* if media or other channel, notify scb that channel open */
  /* look up scb in stream routing table by ccb, tcid */
  p_scb = avdtp_cb.ad.LookupAvdtpScb(*p_tbl);
  if (p_scb == nullptr) {
    AVDT_TRACE_ERROR("%s: Cannot find AvdtScb entry: ccb_idx:%d tcid:%d",
                     __func__, p_tbl->ccb_idx, p_tbl->tcid);
    return;
  }
  /* put lcid in event data */
  open.peer_mtu = p_tbl->peer_mtu;
  open.lcid = avdtp_cb.ad.rt_tbl[p_tbl->ccb_idx][p_tbl->tcid].lcid;
  open.hdr.err_code = avdt_ad_tcid_to_type(p_tbl->tcid);
  tAVDT_SCB_EVT avdt_scb_evt;
  avdt_scb_evt.open = open;
  avdt_scb_event(p_scb, AVDT_SCB_TC_OPEN_EVT, &avdt_scb_evt);
}

/*******************************************************************************
 *
 * Function         avdt_ad_tc_cong_ind
 *
 * Description      This function is called by the L2CAP interface layer when
 *                  L2CAP calls the congestion callback.  It looks up the CCB
 *                  or SCB for the channel and sends it a congestion event.
 *                  The is_congested parameter is the same value passed by
 *                  the L2CAP callback function.
 *
 *
 * Returns          Nothing.
 *
 ******************************************************************************/
void avdt_ad_tc_cong_ind(AvdtpTransportChannel* p_tbl, bool is_congested) {
  AvdtpCcb* p_ccb;
  AvdtpScb* p_scb;

  /* if signaling channel, notify ccb of congestion */
  if (p_tbl->tcid == 0) {
    p_ccb = avdt_ccb_by_idx(p_tbl->ccb_idx);
    tAVDT_CCB_EVT avdt_ccb_evt;
    avdt_ccb_evt.llcong = is_congested;
    avdt_ccb_event(p_ccb, AVDT_CCB_LL_CONG_EVT, &avdt_ccb_evt);
    return;
  }
  /* if media or other channel, notify scb that channel open */
  /* look up scb in stream routing table by ccb, tcid */
  p_scb = avdtp_cb.ad.LookupAvdtpScb(*p_tbl);
  if (p_scb == nullptr) {
    AVDT_TRACE_ERROR("%s: Cannot find AvdtScb entry: ccb_idx:%d tcid:%d",
                     __func__, p_tbl->ccb_idx, p_tbl->tcid);
    return;
  }
  tAVDT_SCB_EVT avdt_scb_evt;
  avdt_scb_evt.llcong = is_congested;
  avdt_scb_event(p_scb, AVDT_SCB_TC_CONG_EVT, &avdt_scb_evt);
}

/*******************************************************************************
 *
 * Function         avdt_ad_tc_data_ind
 *
 * Description      This function is called by the L2CAP interface layer when
 *                  incoming data is received from L2CAP.  It looks up the CCB
 *                  or SCB for the channel and routes the data accordingly.
 *
 *
 * Returns          Nothing.
 *
 ******************************************************************************/
void avdt_ad_tc_data_ind(AvdtpTransportChannel* p_tbl, BT_HDR* p_buf) {
  AvdtpCcb* p_ccb;
  AvdtpScb* p_scb;

  /* store type (media, recovery, reporting) */
  p_buf->layer_specific = avdt_ad_tcid_to_type(p_tbl->tcid);

  /* if signaling channel, handle control message */
  if (p_tbl->tcid == 0) {
    p_ccb = avdt_ccb_by_idx(p_tbl->ccb_idx);
    avdt_msg_ind(p_ccb, p_buf);
    return;
  }
  /* if media or other channel, send event to scb */
  p_scb = avdtp_cb.ad.LookupAvdtpScb(*p_tbl);
  if (p_scb == nullptr) {
    AVDT_TRACE_ERROR("%s: Cannot find AvdtScb entry: ccb_idx:%d tcid:%d",
                     __func__, p_tbl->ccb_idx, p_tbl->tcid);
    osi_free(p_buf);
    AVDT_TRACE_ERROR("%s: buffer freed", __func__);
    return;
  }
  avdt_scb_event(p_scb, AVDT_SCB_TC_DATA_EVT, (tAVDT_SCB_EVT*)&p_buf);
}

/*******************************************************************************
 *
 * Function         avdt_ad_write_req
 *
 * Description      This function is called by a CCB or SCB to send data to a
 *                  transport channel.  It looks up the LCID of the channel
 *                  based on the type, CCB, and SCB (if present).  Then it
 *                  passes the data to L2CA_DataWrite().
 *
 *
 * Returns          AVDT_AD_SUCCESS, if data accepted
 *                  AVDT_AD_CONGESTED, if data accepted and the channel is
 *                                     congested
 *                  AVDT_AD_FAILED, if error
 *
 ******************************************************************************/
uint8_t avdt_ad_write_req(uint8_t type, AvdtpCcb* p_ccb, AvdtpScb* p_scb,
                          BT_HDR* p_buf) {
  uint8_t tcid;

  /* get tcid from type, scb */
  tcid = avdt_ad_type_to_tcid(type, p_scb);

  return L2CA_DataWrite(avdtp_cb.ad.rt_tbl[avdt_ccb_to_idx(p_ccb)][tcid].lcid,
                        p_buf);
}

/*******************************************************************************
 *
 * Function         avdt_ad_open_req
 *
 * Description      This function is called by a CCB or SCB to open a transport
 *                  channel.  This function allocates and initializes a
 *                  transport channel table entry.  The channel can be opened
 *                  in two roles:  as an initiator or acceptor.  When opened
 *                  as an initiator the function will start an L2CAP connection.
 *                  When opened as an acceptor the function simply configures
 *                  the table entry to listen for an incoming channel.
 *
 *
 * Returns          Nothing.
 *
 ******************************************************************************/
void avdt_ad_open_req(uint8_t type, AvdtpCcb* p_ccb, AvdtpScb* p_scb,
                      uint8_t role) {
  AvdtpTransportChannel* p_tbl;
  uint16_t lcid;

  p_tbl = avdt_ad_tc_tbl_alloc(p_ccb);
  if (p_tbl == NULL) {
    AVDT_TRACE_ERROR("avdt_ad_open_req: Cannot allocate p_tbl");
    return;
  }

  p_tbl->tcid = avdt_ad_type_to_tcid(type, p_scb);
  AVDT_TRACE_DEBUG("avdt_ad_open_req: type: %d, role: %d, tcid:%d", type, role,
                   p_tbl->tcid);

  if (type == AVDT_CHAN_SIG) {
    /* if signaling, get mtu from registration control block */
    p_tbl->my_mtu = avdtp_cb.rcb.ctrl_mtu;
    p_tbl->my_flush_to = L2CAP_DEFAULT_FLUSH_TO;
  } else {
    /* otherwise get mtu from scb */
    p_tbl->my_mtu = p_scb->stream_config.mtu;
    p_tbl->my_flush_to = p_scb->stream_config.flush_to;

    /* also set scb_hdl in rt_tbl */
    avdtp_cb.ad.rt_tbl[avdt_ccb_to_idx(p_ccb)][p_tbl->tcid].scb_hdl =
        avdt_scb_to_hdl(p_scb);
    AVDT_TRACE_DEBUG("avdtp_cb.ad.rt_tbl[%d][%d].scb_hdl = %d",
                     avdt_ccb_to_idx(p_ccb), p_tbl->tcid,
                     avdt_scb_to_hdl(p_scb));
  }

  /* if we're acceptor, we're done; just sit back and listen */
  if (role == AVDT_ACP) {
    p_tbl->state = AVDT_AD_ST_ACP;
  }
  /* else we're inititator, start the L2CAP connection */
  else {
    p_tbl->state = AVDT_AD_ST_CONN;

    /* call l2cap connect req */
    lcid = L2CA_ConnectReq(AVDT_PSM, p_ccb->peer_addr);
    if (lcid != 0) {
      /* if connect req ok, store tcid in lcid table  */
      avdtp_cb.ad.lcid_tbl[lcid - L2CAP_BASE_APPL_CID] =
          avdt_ad_tc_tbl_to_idx(p_tbl);
      AVDT_TRACE_DEBUG("avdtp_cb.ad.lcid_tbl[%d] = %d",
                       (lcid - L2CAP_BASE_APPL_CID),
                       avdt_ad_tc_tbl_to_idx(p_tbl));

      avdtp_cb.ad.rt_tbl[avdt_ccb_to_idx(p_ccb)][p_tbl->tcid].lcid = lcid;
      AVDT_TRACE_DEBUG("avdtp_cb.ad.rt_tbl[%d][%d].lcid = 0x%x",
                       avdt_ccb_to_idx(p_ccb), p_tbl->tcid, lcid);
    } else {
      /* if connect req failed, call avdt_ad_tc_close_ind() */
      avdt_ad_tc_close_ind(p_tbl, 0);
    }
  }
}

/*******************************************************************************
 *
 * Function         avdt_ad_close_req
 *
 * Description      This function is called by a CCB or SCB to close a
 *                  transport channel.  The function looks up the LCID for the
 *                  channel and calls L2CA_DisconnectReq().
 *
 *
 * Returns          Nothing.
 *
 ******************************************************************************/
void avdt_ad_close_req(uint8_t type, AvdtpCcb* p_ccb, AvdtpScb* p_scb) {
  uint8_t tcid;
  AvdtpTransportChannel* p_tbl;

  p_tbl = avdt_ad_tc_tbl_by_type(type, p_ccb, p_scb);
  AVDT_TRACE_DEBUG("avdt_ad_close_req state: %d", p_tbl->state);

  switch (p_tbl->state) {
    case AVDT_AD_ST_UNUSED:
      /* probably for reporting */
      break;
    case AVDT_AD_ST_ACP:
      /* if we're listening on this channel, send ourselves a close ind */
      avdt_ad_tc_close_ind(p_tbl, 0);
      break;
    default:
      /* get tcid from type, scb */
      tcid = avdt_ad_type_to_tcid(type, p_scb);

      /* call l2cap disconnect req */
      L2CA_DisconnectReq(avdtp_cb.ad.rt_tbl[avdt_ccb_to_idx(p_ccb)][tcid].lcid);
  }
}