| /****************************************************************************** |
| * |
| * 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 <bluetooth/log.h> |
| #include <string.h> |
| |
| #include "avct_int.h" |
| #include "bta/include/bta_sec_api.h" |
| #include "internal_include/bt_target.h" |
| #include "l2c_api.h" |
| #include "l2cdefs.h" |
| #include "osi/include/allocator.h" |
| #include "stack/include/bt_hdr.h" |
| #include "types/raw_address.h" |
| |
| using namespace bluetooth; |
| |
| /* 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() { |
| log::verbose("AVCT_Register"); |
| |
| /* initialize AVCTP data structures */ |
| memset(&avct_cb, 0, sizeof(tAVCT_CB)); |
| |
| /* register PSM with L2CAP */ |
| L2CA_Register2(AVCT_PSM, avct_l2c_appl, true /* enable_snoop */, nullptr, |
| kAvrcMtu, 0, BTA_SEC_AUTHENTICATE); |
| |
| /* Include the browsing channel which uses eFCR */ |
| tL2CAP_ERTM_INFO ertm_info; |
| ertm_info.preferred_mode = L2CAP_FCR_ERTM_MODE; |
| |
| L2CA_Register2(AVCT_BR_PSM, avct_l2c_br_appl, true /*enable_snoop*/, |
| &ertm_info, kAvrcBrMtu, AVCT_MIN_BROWSE_MTU, |
| BTA_SEC_AUTHENTICATE); |
| } |
| |
| /******************************************************************************* |
| * |
| * 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) { |
| log::verbose("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; |
| |
| log::verbose("AVCT_CreateConn: {}, control:{}", 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; |
| log::verbose("ch_state: {}", 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; |
| |
| log::verbose("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; |
| |
| log::verbose("AVCT_CreateBrowse: {}", 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; |
| log::verbose("ch_state: {}", 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; |
| |
| log::verbose("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; |
| |
| log::verbose(""); |
| |
| /* verify p_msg parameter */ |
| if (p_msg == NULL) { |
| return AVCT_NO_RESOURCES; |
| } |
| log::verbose("len: {} layer_specific: {}", 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; |
| } |