/****************************************************************************** * * Copyright 2003-2016 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 API of the audio/video control transport protocol. * ******************************************************************************/ #include "avct_api.h" #include <string.h> #include "avct_int.h" #include "bt_common.h" #include "bt_target.h" #include "bt_types.h" #include "bt_utils.h" #include "btm_api.h" #include "l2c_api.h" #include "l2cdefs.h" #include "osi/include/osi.h" /* Control block for AVCT */ tAVCT_CB avct_cb; /******************************************************************************* * * Function AVCT_Register * * Description This is the system level registration function for the * AVCTP protocol. This function initializes AVCTP and * prepares the protocol stack for its use. This function * must be called once by the system or platform using AVCTP * before the other functions of the API an be used. * * * Returns void * ******************************************************************************/ void AVCT_Register(uint16_t mtu, UNUSED_ATTR uint16_t mtu_br, uint8_t sec_mask) { AVCT_TRACE_API("AVCT_Register"); /* register PSM with L2CAP */ L2CA_Register(AVCT_PSM, (tL2CAP_APPL_INFO*)&avct_l2c_appl, true /* enable_snoop */); /* set security level */ BTM_SetSecurityLevel(true, "", BTM_SEC_SERVICE_AVCTP, sec_mask, AVCT_PSM, 0, 0); BTM_SetSecurityLevel(false, "", BTM_SEC_SERVICE_AVCTP, sec_mask, AVCT_PSM, 0, 0); /* initialize AVCTP data structures */ memset(&avct_cb, 0, sizeof(tAVCT_CB)); /* Include the browsing channel which uses eFCR */ L2CA_Register(AVCT_BR_PSM, (tL2CAP_APPL_INFO*)&avct_l2c_br_appl, true /*enable_snoop*/); /* AVCTP browsing channel uses the same security service as AVCTP control * channel */ BTM_SetSecurityLevel(true, "", BTM_SEC_SERVICE_AVCTP, sec_mask, AVCT_BR_PSM, 0, 0); BTM_SetSecurityLevel(false, "", BTM_SEC_SERVICE_AVCTP, sec_mask, AVCT_BR_PSM, 0, 0); if (mtu_br < AVCT_MIN_BROWSE_MTU) mtu_br = AVCT_MIN_BROWSE_MTU; avct_cb.mtu_br = mtu_br; #if defined(AVCT_INITIAL_TRACE_LEVEL) avct_cb.trace_level = AVCT_INITIAL_TRACE_LEVEL; #else avct_cb.trace_level = BT_TRACE_LEVEL_NONE; #endif if (mtu < AVCT_MIN_CONTROL_MTU) mtu = AVCT_MIN_CONTROL_MTU; /* store mtu */ avct_cb.mtu = mtu; } /******************************************************************************* * * Function AVCT_Deregister * * Description This function is called to deregister use AVCTP protocol. * It is called when AVCTP is no longer being used by any * application in the system. Before this function can be * called, all connections must be removed with * AVCT_RemoveConn(). * * * Returns void * ******************************************************************************/ void AVCT_Deregister(void) { AVCT_TRACE_API("AVCT_Deregister"); /* deregister PSM with L2CAP */ L2CA_Deregister(AVCT_PSM); } /******************************************************************************* * * Function AVCT_CreateConn * * Description Create an AVCTP connection. There are two types of * connections, initiator and acceptor, as determined by * the p_cc->role parameter. When this function is called to * create an initiator connection, an AVCTP connection to * the peer device is initiated if one does not already exist. * If an acceptor connection is created, the connection waits * passively for an incoming AVCTP connection from a peer * device. * * * Returns AVCT_SUCCESS if successful, otherwise error. * ******************************************************************************/ uint16_t AVCT_CreateConn(uint8_t* p_handle, tAVCT_CC* p_cc, const RawAddress& peer_addr) { uint16_t result = AVCT_SUCCESS; tAVCT_CCB* p_ccb; tAVCT_LCB* p_lcb; AVCT_TRACE_API("AVCT_CreateConn: %d, control:%d", p_cc->role, p_cc->control); /* Allocate ccb; if no ccbs, return failure */ p_ccb = avct_ccb_alloc(p_cc); if (p_ccb == NULL) { result = AVCT_NO_RESOURCES; } else { /* get handle */ *p_handle = avct_ccb_to_idx(p_ccb); /* if initiator connection */ if (p_cc->role == AVCT_INT) { /* find link; if none allocate a new one */ p_lcb = avct_lcb_by_bd(peer_addr); if (p_lcb == NULL) { p_lcb = avct_lcb_alloc(peer_addr); if (p_lcb == NULL) { /* no link resources; free ccb as well */ avct_ccb_dealloc(p_ccb, AVCT_NO_EVT, 0, NULL); result = AVCT_NO_RESOURCES; } } /* check if PID already in use */ else if (avct_lcb_has_pid(p_lcb, p_cc->pid)) { avct_ccb_dealloc(p_ccb, AVCT_NO_EVT, 0, NULL); result = AVCT_PID_IN_USE; } if (result == AVCT_SUCCESS) { /* bind lcb to ccb */ p_ccb->p_lcb = p_lcb; AVCT_TRACE_DEBUG("ch_state: %d", p_lcb->ch_state); tAVCT_LCB_EVT avct_lcb_evt; avct_lcb_evt.p_ccb = p_ccb; avct_lcb_event(p_lcb, AVCT_LCB_UL_BIND_EVT, &avct_lcb_evt); } } } return result; } /******************************************************************************* * * Function AVCT_RemoveConn * * Description Remove an AVCTP connection. This function is called when * the application is no longer using a connection. If this * is the last connection to a peer the L2CAP channel for AVCTP * will be closed. * * * Returns AVCT_SUCCESS if successful, otherwise error. * ******************************************************************************/ uint16_t AVCT_RemoveConn(uint8_t handle) { uint16_t result = AVCT_SUCCESS; tAVCT_CCB* p_ccb; AVCT_TRACE_API("AVCT_RemoveConn"); /* map handle to ccb */ p_ccb = avct_ccb_by_idx(handle); if (p_ccb == NULL) { result = AVCT_BAD_HANDLE; } /* if connection not bound to lcb, dealloc */ else if (p_ccb->p_lcb == NULL) { avct_ccb_dealloc(p_ccb, AVCT_NO_EVT, 0, NULL); } /* send unbind event to lcb */ else { tAVCT_LCB_EVT avct_lcb_evt; avct_lcb_evt.p_ccb = p_ccb; avct_lcb_event(p_ccb->p_lcb, AVCT_LCB_UL_UNBIND_EVT, &avct_lcb_evt); } return result; } /******************************************************************************* * * Function AVCT_CreateBrowse * * Description Create an AVCTP Browse channel. There are two types of * connections, initiator and acceptor, as determined by * the role parameter. When this function is called to * create an initiator connection, the Browse channel to * the peer device is initiated if one does not already exist. * If an acceptor connection is created, the connection waits * passively for an incoming AVCTP connection from a peer * device. * * * Returns AVCT_SUCCESS if successful, otherwise error. * ******************************************************************************/ uint16_t AVCT_CreateBrowse(uint8_t handle, uint8_t role) { uint16_t result = AVCT_SUCCESS; tAVCT_CCB* p_ccb; tAVCT_BCB* p_bcb; int index; AVCT_TRACE_API("AVCT_CreateBrowse: %d", role); /* map handle to ccb */ p_ccb = avct_ccb_by_idx(handle); if (p_ccb == NULL) { return AVCT_BAD_HANDLE; } else { /* mark this CCB as supporting browsing channel */ if ((p_ccb->allocated & AVCT_ALOC_BCB) == 0) { p_ccb->allocated |= AVCT_ALOC_BCB; } } /* if initiator connection */ if (role == AVCT_INT) { /* the link control block must exist before this function is called as INT. */ if ((p_ccb->p_lcb == NULL) || (p_ccb->p_lcb->allocated == 0)) { result = AVCT_NOT_OPEN; } else { /* find link; if none allocate a new one */ index = p_ccb->p_lcb->allocated; if (index > AVCT_NUM_LINKS) { result = AVCT_BAD_HANDLE; } else { p_bcb = &avct_cb.bcb[index - 1]; p_bcb->allocated = index; } } if (result == AVCT_SUCCESS) { /* bind bcb to ccb */ p_ccb->p_bcb = p_bcb; p_bcb->peer_addr = p_ccb->p_lcb->peer_addr; AVCT_TRACE_DEBUG("ch_state: %d", p_bcb->ch_state); tAVCT_LCB_EVT avct_lcb_evt; avct_lcb_evt.p_ccb = p_ccb; avct_bcb_event(p_bcb, AVCT_LCB_UL_BIND_EVT, &avct_lcb_evt); } } return result; } /******************************************************************************* * * Function AVCT_RemoveBrowse * * Description Remove an AVCTP Browse channel. This function is called * when the application is no longer using a connection. If * this is the last connection to a peer the L2CAP channel for * AVCTP will be closed. * * * Returns AVCT_SUCCESS if successful, otherwise error. * ******************************************************************************/ uint16_t AVCT_RemoveBrowse(uint8_t handle) { uint16_t result = AVCT_SUCCESS; tAVCT_CCB* p_ccb; AVCT_TRACE_API("AVCT_RemoveBrowse"); /* map handle to ccb */ p_ccb = avct_ccb_by_idx(handle); if (p_ccb == NULL) { result = AVCT_BAD_HANDLE; } else if (p_ccb->p_bcb != NULL) /* send unbind event to bcb */ { tAVCT_LCB_EVT avct_lcb_evt; avct_lcb_evt.p_ccb = p_ccb; avct_bcb_event(p_ccb->p_bcb, AVCT_LCB_UL_UNBIND_EVT, &avct_lcb_evt); } return result; } /******************************************************************************* * * Function AVCT_GetBrowseMtu * * Description Get the peer_mtu for the AVCTP Browse channel of the given * connection. * * Returns the peer browsing channel MTU. * ******************************************************************************/ uint16_t AVCT_GetBrowseMtu(uint8_t handle) { uint16_t peer_mtu = AVCT_MIN_BROWSE_MTU; tAVCT_CCB* p_ccb = avct_ccb_by_idx(handle); if (p_ccb != NULL && p_ccb->p_bcb != NULL) { peer_mtu = p_ccb->p_bcb->peer_mtu; } return peer_mtu; } /******************************************************************************* * * Function AVCT_GetPeerMtu * * Description Get the peer_mtu for the AVCTP channel of the given * connection. * * Returns the peer MTU size. * ******************************************************************************/ uint16_t AVCT_GetPeerMtu(uint8_t handle) { uint16_t peer_mtu = L2CAP_DEFAULT_MTU; tAVCT_CCB* p_ccb; /* map handle to ccb */ p_ccb = avct_ccb_by_idx(handle); if (p_ccb != NULL) { if (p_ccb->p_lcb) { peer_mtu = p_ccb->p_lcb->peer_mtu; } } return peer_mtu; } /******************************************************************************* * * Function AVCT_MsgReq * * Description Send an AVCTP message to a peer device. In calling * AVCT_MsgReq(), the application should keep track of the * congestion state of AVCTP as communicated with events * AVCT_CONG_IND_EVT and AVCT_UNCONG_IND_EVT. If the * application calls AVCT_MsgReq() when AVCTP is congested * the message may be discarded. The application may make its * first call to AVCT_MsgReq() after it receives an * AVCT_CONNECT_CFM_EVT or AVCT_CONNECT_IND_EVT on control * channel or AVCT_BROWSE_CONN_CFM_EVT or * AVCT_BROWSE_CONN_IND_EVT on browsing channel. * * p_msg->layer_specific must be set to * AVCT_DATA_CTRL for control channel traffic; * AVCT_DATA_BROWSE for for browse channel traffic. * * Returns AVCT_SUCCESS if successful, otherwise error. * ******************************************************************************/ uint16_t AVCT_MsgReq(uint8_t handle, uint8_t label, uint8_t cr, BT_HDR* p_msg) { uint16_t result = AVCT_SUCCESS; tAVCT_CCB* p_ccb; tAVCT_UL_MSG ul_msg; AVCT_TRACE_API("%s", __func__); /* verify p_msg parameter */ if (p_msg == NULL) { return AVCT_NO_RESOURCES; } AVCT_TRACE_API("%s len: %d layer_specific: %d", __func__, p_msg->len, p_msg->layer_specific); /* map handle to ccb */ p_ccb = avct_ccb_by_idx(handle); if (p_ccb == NULL) { result = AVCT_BAD_HANDLE; osi_free(p_msg); } /* verify channel is bound to link */ else if (p_ccb->p_lcb == NULL) { result = AVCT_NOT_OPEN; osi_free(p_msg); } if (result == AVCT_SUCCESS) { ul_msg.p_buf = p_msg; ul_msg.p_ccb = p_ccb; ul_msg.label = label; ul_msg.cr = cr; /* send msg event to bcb */ if (p_msg->layer_specific == AVCT_DATA_BROWSE) { if (p_ccb->p_bcb == NULL && (p_ccb->allocated & AVCT_ALOC_BCB) == 0) { /* BCB channel is not open and not allocated */ result = AVCT_BAD_HANDLE; osi_free(p_msg); } else { p_ccb->p_bcb = avct_bcb_by_lcb(p_ccb->p_lcb); tAVCT_LCB_EVT avct_lcb_evt; avct_lcb_evt.ul_msg = ul_msg; avct_bcb_event(p_ccb->p_bcb, AVCT_LCB_UL_MSG_EVT, &avct_lcb_evt); } } /* send msg event to lcb */ else { tAVCT_LCB_EVT avct_lcb_evt; avct_lcb_evt.ul_msg = ul_msg; avct_lcb_event(p_ccb->p_lcb, AVCT_LCB_UL_MSG_EVT, &avct_lcb_evt); } } return result; }