| /****************************************************************************** |
| * |
| * 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 API of the audio/video distribution transport |
| * protocol. |
| * |
| ******************************************************************************/ |
| |
| #include "avdt_api.h" |
| #include <string.h> |
| #include "avdt_int.h" |
| #include "avdtc_api.h" |
| #include "bt_target.h" |
| #include "bt_types.h" |
| #include "btm_api.h" |
| #include "btu.h" |
| #include "l2c_api.h" |
| #include "stack/include/a2dp_codec_api.h" |
| |
| /* Control block for AVDT */ |
| tAVDT_CB avdt_cb; |
| |
| void avdt_ccb_idle_ccb_timer_timeout(void* data) { |
| tAVDT_CCB* p_ccb = (tAVDT_CCB*)data; |
| uint8_t avdt_event = AVDT_CCB_IDLE_TOUT_EVT; |
| uint8_t err_code = AVDT_ERR_TIMEOUT; |
| |
| tAVDT_CCB_EVT avdt_ccb_evt; |
| avdt_ccb_evt.err_code = err_code; |
| avdt_ccb_event(p_ccb, avdt_event, &avdt_ccb_evt); |
| } |
| |
| void avdt_ccb_ret_ccb_timer_timeout(void* data) { |
| tAVDT_CCB* p_ccb = (tAVDT_CCB*)data; |
| uint8_t avdt_event = AVDT_CCB_RET_TOUT_EVT; |
| uint8_t err_code = AVDT_ERR_TIMEOUT; |
| |
| tAVDT_CCB_EVT avdt_ccb_evt; |
| avdt_ccb_evt.err_code = err_code; |
| avdt_ccb_event(p_ccb, avdt_event, &avdt_ccb_evt); |
| } |
| |
| void avdt_ccb_rsp_ccb_timer_timeout(void* data) { |
| tAVDT_CCB* p_ccb = (tAVDT_CCB*)data; |
| uint8_t avdt_event = AVDT_CCB_RSP_TOUT_EVT; |
| uint8_t err_code = AVDT_ERR_TIMEOUT; |
| |
| tAVDT_CCB_EVT avdt_ccb_evt; |
| avdt_ccb_evt.err_code = err_code; |
| avdt_ccb_event(p_ccb, avdt_event, &avdt_ccb_evt); |
| } |
| |
| void avdt_scb_transport_channel_timer_timeout(void* data) { |
| tAVDT_SCB* p_scb = (tAVDT_SCB*)data; |
| uint8_t avdt_event = AVDT_SCB_TC_TOUT_EVT; |
| |
| avdt_scb_event(p_scb, avdt_event, NULL); |
| } |
| |
| /******************************************************************************* |
| * |
| * Function AVDT_Register |
| * |
| * Description This is the system level registration function for the |
| * AVDTP protocol. This function initializes AVDTP and |
| * prepares the protocol stack for its use. This function |
| * must be called once by the system or platform using AVDTP |
| * before the other functions of the API an be used. |
| * |
| * |
| * Returns void |
| * |
| ******************************************************************************/ |
| void AVDT_Register(tAVDT_REG* p_reg, tAVDT_CTRL_CBACK* p_cback) { |
| /* register PSM with L2CAP */ |
| L2CA_Register(AVDT_PSM, (tL2CAP_APPL_INFO*)&avdt_l2c_appl); |
| |
| /* set security level */ |
| BTM_SetSecurityLevel(true, "", BTM_SEC_SERVICE_AVDTP, p_reg->sec_mask, |
| AVDT_PSM, BTM_SEC_PROTO_AVDT, AVDT_CHAN_SIG); |
| BTM_SetSecurityLevel(false, "", BTM_SEC_SERVICE_AVDTP, p_reg->sec_mask, |
| AVDT_PSM, BTM_SEC_PROTO_AVDT, AVDT_CHAN_SIG); |
| |
| /* do not use security on the media channel */ |
| BTM_SetSecurityLevel(true, "", BTM_SEC_SERVICE_AVDTP_NOSEC, BTM_SEC_NONE, |
| AVDT_PSM, BTM_SEC_PROTO_AVDT, AVDT_CHAN_MEDIA); |
| BTM_SetSecurityLevel(false, "", BTM_SEC_SERVICE_AVDTP_NOSEC, BTM_SEC_NONE, |
| AVDT_PSM, BTM_SEC_PROTO_AVDT, AVDT_CHAN_MEDIA); |
| |
| #if (AVDT_REPORTING == TRUE) |
| /* do not use security on the reporting channel */ |
| BTM_SetSecurityLevel(true, "", BTM_SEC_SERVICE_AVDTP_NOSEC, BTM_SEC_NONE, |
| AVDT_PSM, BTM_SEC_PROTO_AVDT, AVDT_CHAN_REPORT); |
| BTM_SetSecurityLevel(false, "", BTM_SEC_SERVICE_AVDTP_NOSEC, BTM_SEC_NONE, |
| AVDT_PSM, BTM_SEC_PROTO_AVDT, AVDT_CHAN_REPORT); |
| #endif |
| |
| /* initialize AVDTP data structures */ |
| avdt_scb_init(); |
| avdt_ccb_init(); |
| avdt_ad_init(); |
| |
| /* copy registration struct */ |
| memcpy(&avdt_cb.rcb, p_reg, sizeof(tAVDT_REG)); |
| avdt_cb.p_conn_cback = p_cback; |
| } |
| |
| /******************************************************************************* |
| * |
| * Function AVDT_Deregister |
| * |
| * Description This function is called to deregister use AVDTP protocol. |
| * It is called when AVDTP is no longer being used by any |
| * application in the system. Before this function can be |
| * called, all streams must be removed with |
| * AVDT_RemoveStream(). |
| * |
| * |
| * Returns void |
| * |
| ******************************************************************************/ |
| void AVDT_Deregister(void) { |
| /* deregister PSM with L2CAP */ |
| L2CA_Deregister(AVDT_PSM); |
| } |
| |
| void AVDT_AbortReq(uint8_t handle) { |
| AVDT_TRACE_WARNING("%s: handle=%d", __func__, handle); |
| |
| tAVDT_SCB* p_scb = avdt_scb_by_hdl(handle); |
| if (p_scb != NULL) { |
| avdt_scb_event(p_scb, AVDT_SCB_API_ABORT_REQ_EVT, NULL); |
| } else { |
| AVDT_TRACE_ERROR("%s Improper SCB, can not abort the stream", __func__); |
| } |
| } |
| |
| /******************************************************************************* |
| * |
| * Function AVDT_CreateStream |
| * |
| * Description Create a stream endpoint. After a stream endpoint is |
| * created an application can initiate a connection between |
| * this endpoint and an endpoint on a peer device. In |
| * addition, a peer device can discover, get the capabilities, |
| * and connect to this endpoint. |
| * |
| * |
| * Returns AVDT_SUCCESS if successful, otherwise error. |
| * |
| ******************************************************************************/ |
| uint16_t AVDT_CreateStream(uint8_t* p_handle, tAVDT_CS* p_cs) { |
| uint16_t result = AVDT_SUCCESS; |
| tAVDT_SCB* p_scb; |
| |
| AVDT_TRACE_DEBUG("%s", __func__); |
| |
| /* Verify parameters; if invalid, return failure */ |
| if (((p_cs->cfg.psc_mask & (~AVDT_PSC)) != 0) || |
| (p_cs->p_ctrl_cback == NULL)) { |
| result = AVDT_BAD_PARAMS; |
| } |
| /* Allocate scb; if no scbs, return failure */ |
| else { |
| p_scb = avdt_scb_alloc(p_cs); |
| if (p_scb == NULL) { |
| result = AVDT_NO_RESOURCES; |
| } else { |
| *p_handle = avdt_scb_to_hdl(p_scb); |
| } |
| } |
| |
| AVDT_TRACE_DEBUG("%s: result=%d", __func__, result); |
| |
| return result; |
| } |
| |
| /******************************************************************************* |
| * |
| * Function AVDT_RemoveStream |
| * |
| * Description Remove a stream endpoint. This function is called when |
| * the application is no longer using a stream endpoint. |
| * If this function is called when the endpoint is connected |
| * the connection is closed and then the stream endpoint |
| * is removed. |
| * |
| * |
| * Returns AVDT_SUCCESS if successful, otherwise error. |
| * |
| ******************************************************************************/ |
| uint16_t AVDT_RemoveStream(uint8_t handle) { |
| uint16_t result = AVDT_SUCCESS; |
| tAVDT_SCB* p_scb; |
| |
| AVDT_TRACE_DEBUG("%s: handle=%d", __func__, handle); |
| |
| /* look up scb */ |
| p_scb = avdt_scb_by_hdl(handle); |
| if (p_scb == NULL) { |
| result = AVDT_BAD_HANDLE; |
| } else { |
| /* send remove event to scb */ |
| avdt_scb_event(p_scb, AVDT_SCB_API_REMOVE_EVT, NULL); |
| } |
| |
| AVDT_TRACE_DEBUG("%s: result=%d", __func__, result); |
| |
| return result; |
| } |
| |
| /******************************************************************************* |
| * |
| * Function AVDT_DiscoverReq |
| * |
| * Description This function initiates a connection to the AVDTP service |
| * on the peer device, if not already present, and discovers |
| * the stream endpoints on the peer device. (Please note |
| * that AVDTP discovery is unrelated to SDP discovery). |
| * This function can be called at any time regardless of |
| * whether there is an AVDTP connection to the peer device. |
| * |
| * When discovery is complete, an AVDT_DISCOVER_CFM_EVT |
| * is sent to the application via its callback function. |
| * The application must not call AVDT_GetCapReq() or |
| * AVDT_DiscoverReq() again to the same device until |
| * discovery is complete. |
| * |
| * The memory addressed by sep_info is allocated by the |
| * application. This memory is written to by AVDTP as part |
| * of the discovery procedure. This memory must remain |
| * accessible until the application receives the |
| * AVDT_DISCOVER_CFM_EVT. |
| * |
| * Returns AVDT_SUCCESS if successful, otherwise error. |
| * |
| ******************************************************************************/ |
| uint16_t AVDT_DiscoverReq(const RawAddress& bd_addr, tAVDT_SEP_INFO* p_sep_info, |
| uint8_t max_seps, tAVDT_CTRL_CBACK* p_cback) { |
| tAVDT_CCB* p_ccb; |
| uint16_t result = AVDT_SUCCESS; |
| tAVDT_CCB_EVT evt; |
| |
| AVDT_TRACE_DEBUG("%s", __func__); |
| |
| /* find channel control block for this bd addr; if none, allocate one */ |
| p_ccb = avdt_ccb_by_bd(bd_addr); |
| if (p_ccb == NULL) { |
| p_ccb = avdt_ccb_alloc(bd_addr); |
| if (p_ccb == NULL) { |
| /* could not allocate channel control block */ |
| result = AVDT_NO_RESOURCES; |
| } |
| } |
| |
| if (result == AVDT_SUCCESS) { |
| /* make sure no discovery or get capabilities req already in progress */ |
| if (p_ccb->proc_busy) { |
| result = AVDT_BUSY; |
| } |
| /* send event to ccb */ |
| else { |
| evt.discover.p_sep_info = p_sep_info; |
| evt.discover.num_seps = max_seps; |
| evt.discover.p_cback = p_cback; |
| avdt_ccb_event(p_ccb, AVDT_CCB_API_DISCOVER_REQ_EVT, &evt); |
| } |
| } |
| |
| AVDT_TRACE_DEBUG("%s: result=%d", __func__, result); |
| |
| return result; |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avdt_get_cap_req |
| * |
| * Description internal function to serve both AVDT_GetCapReq and |
| * AVDT_GetAllCapReq |
| * |
| * Returns AVDT_SUCCESS if successful, otherwise error. |
| * |
| ******************************************************************************/ |
| static uint16_t avdt_get_cap_req(const RawAddress& bd_addr, |
| tAVDT_CCB_API_GETCAP* p_evt) { |
| tAVDT_CCB* p_ccb = NULL; |
| uint16_t result = AVDT_SUCCESS; |
| |
| AVDT_TRACE_DEBUG("%s", __func__); |
| |
| /* verify SEID */ |
| if ((p_evt->single.seid < AVDT_SEID_MIN) || |
| (p_evt->single.seid > AVDT_SEID_MAX)) { |
| AVDT_TRACE_ERROR("seid: %d", p_evt->single.seid); |
| result = AVDT_BAD_PARAMS; |
| } |
| /* find channel control block for this bd addr; if none, allocate one */ |
| else { |
| p_ccb = avdt_ccb_by_bd(bd_addr); |
| if (p_ccb == NULL) { |
| p_ccb = avdt_ccb_alloc(bd_addr); |
| if (p_ccb == NULL) { |
| /* could not allocate channel control block */ |
| result = AVDT_NO_RESOURCES; |
| } |
| } |
| } |
| |
| if (result == AVDT_SUCCESS) { |
| /* make sure no discovery or get capabilities req already in progress */ |
| if (p_ccb->proc_busy) { |
| result = AVDT_BUSY; |
| } |
| /* send event to ccb */ |
| else { |
| avdt_ccb_event(p_ccb, AVDT_CCB_API_GETCAP_REQ_EVT, (tAVDT_CCB_EVT*)p_evt); |
| } |
| } |
| |
| AVDT_TRACE_DEBUG("%s: result=%d", __func__, result); |
| |
| return result; |
| } |
| |
| /******************************************************************************* |
| * |
| * Function AVDT_GetCapReq |
| * |
| * Description This function initiates a connection to the AVDTP service |
| * on the peer device, if not already present, and gets the |
| * capabilities of a stream endpoint on the peer device. |
| * This function can be called at any time regardless of |
| * whether there is an AVDTP connection to the peer device. |
| * |
| * When the procedure is complete, an AVDT_GETCAP_CFM_EVT is |
| * sent to the application via its callback function. The |
| * application must not call AVDT_GetCapReq() or |
| * AVDT_DiscoverReq() again until the procedure is complete. |
| * |
| * The memory pointed to by p_cfg is allocated by the |
| * application. This memory is written to by AVDTP as part |
| * of the get capabilities procedure. This memory must |
| * remain accessible until the application receives |
| * the AVDT_GETCAP_CFM_EVT. |
| * |
| * Returns AVDT_SUCCESS if successful, otherwise error. |
| * |
| ******************************************************************************/ |
| uint16_t AVDT_GetCapReq(const RawAddress& bd_addr, uint8_t seid, |
| tAVDT_CFG* p_cfg, tAVDT_CTRL_CBACK* p_cback) { |
| tAVDT_CCB_API_GETCAP getcap; |
| uint16_t result = AVDT_SUCCESS; |
| |
| AVDT_TRACE_DEBUG("%s", __func__); |
| |
| getcap.single.seid = seid; |
| getcap.single.sig_id = AVDT_SIG_GETCAP; |
| getcap.p_cfg = p_cfg; |
| getcap.p_cback = p_cback; |
| result = avdt_get_cap_req(bd_addr, &getcap); |
| |
| AVDT_TRACE_DEBUG("%s: result=%d", __func__, result); |
| |
| return result; |
| } |
| |
| /******************************************************************************* |
| * |
| * Function AVDT_GetAllCapReq |
| * |
| * Description This function initiates a connection to the AVDTP service |
| * on the peer device, if not already present, and gets the |
| * capabilities of a stream endpoint on the peer device. |
| * This function can be called at any time regardless of |
| * whether there is an AVDTP connection to the peer device. |
| * |
| * When the procedure is complete, an AVDT_GETCAP_CFM_EVT is |
| * sent to the application via its callback function. The |
| * application must not call AVDT_GetCapReq() or |
| * AVDT_DiscoverReq() again until the procedure is complete. |
| * |
| * The memory pointed to by p_cfg is allocated by the |
| * application. This memory is written to by AVDTP as part |
| * of the get capabilities procedure. This memory must |
| * remain accessible until the application receives |
| * the AVDT_GETCAP_CFM_EVT. |
| * |
| * Returns AVDT_SUCCESS if successful, otherwise error. |
| * |
| ******************************************************************************/ |
| uint16_t AVDT_GetAllCapReq(const RawAddress& bd_addr, uint8_t seid, |
| tAVDT_CFG* p_cfg, tAVDT_CTRL_CBACK* p_cback) { |
| tAVDT_CCB_API_GETCAP getcap; |
| uint16_t result = AVDT_SUCCESS; |
| |
| AVDT_TRACE_DEBUG("%s", __func__); |
| |
| getcap.single.seid = seid; |
| getcap.single.sig_id = AVDT_SIG_GET_ALLCAP; |
| getcap.p_cfg = p_cfg; |
| getcap.p_cback = p_cback; |
| result = avdt_get_cap_req(bd_addr, &getcap); |
| |
| AVDT_TRACE_DEBUG("%s: result=%d", __func__, result); |
| |
| return result; |
| } |
| |
| /******************************************************************************* |
| * |
| * Function AVDT_DelayReport |
| * |
| * Description This functions sends a Delay Report to the peer device |
| * that is associated with a particular SEID. |
| * This function is called by SNK device. |
| * |
| * Returns AVDT_SUCCESS if successful, otherwise error. |
| * |
| ******************************************************************************/ |
| uint16_t AVDT_DelayReport(uint8_t handle, uint8_t seid, uint16_t delay) { |
| tAVDT_SCB* p_scb; |
| uint16_t result = AVDT_SUCCESS; |
| tAVDT_SCB_EVT evt; |
| |
| AVDT_TRACE_DEBUG("%s: handle=%d ceid=%d delay=%d", __func__, handle, seid, |
| delay); |
| |
| /* map handle to scb */ |
| p_scb = avdt_scb_by_hdl(handle); |
| if (p_scb == NULL) { |
| result = AVDT_BAD_HANDLE; |
| } else |
| /* send event to scb */ |
| { |
| evt.apidelay.hdr.seid = seid; |
| evt.apidelay.delay = delay; |
| avdt_scb_event(p_scb, AVDT_SCB_API_DELAY_RPT_REQ_EVT, &evt); |
| } |
| |
| AVDT_TRACE_DEBUG("%s: result=%d", __func__, result); |
| |
| return result; |
| } |
| |
| /******************************************************************************* |
| * |
| * Function AVDT_OpenReq |
| * |
| * Description This function initiates a connection to the AVDTP service |
| * on the peer device, if not already present, and connects |
| * to a stream endpoint on a peer device. When the connection |
| * is completed, an AVDT_OPEN_CFM_EVT is sent to the |
| * application via the control callback function for this |
| * handle. |
| * |
| * Returns AVDT_SUCCESS if successful, otherwise error. |
| * |
| ******************************************************************************/ |
| uint16_t AVDT_OpenReq(uint8_t handle, const RawAddress& bd_addr, uint8_t seid, |
| tAVDT_CFG* p_cfg) { |
| tAVDT_CCB* p_ccb = NULL; |
| tAVDT_SCB* p_scb = NULL; |
| uint16_t result = AVDT_SUCCESS; |
| tAVDT_SCB_EVT evt; |
| |
| AVDT_TRACE_DEBUG("%s: handle=%d seid=%d", __func__, handle, seid); |
| |
| /* verify SEID */ |
| if ((seid < AVDT_SEID_MIN) || (seid > AVDT_SEID_MAX)) { |
| result = AVDT_BAD_PARAMS; |
| } |
| /* map handle to scb */ |
| else { |
| p_scb = avdt_scb_by_hdl(handle); |
| if (p_scb == NULL) { |
| result = AVDT_BAD_HANDLE; |
| } |
| /* find channel control block for this bd addr; if none, allocate one */ |
| else { |
| p_ccb = avdt_ccb_by_bd(bd_addr); |
| if (p_ccb == NULL) { |
| p_ccb = avdt_ccb_alloc(bd_addr); |
| if (p_ccb == NULL) { |
| /* could not allocate channel control block */ |
| result = AVDT_NO_RESOURCES; |
| } |
| } |
| } |
| } |
| |
| /* send event to scb */ |
| if (result == AVDT_SUCCESS) { |
| A2DP_DumpCodecInfo(p_cfg->codec_info); |
| |
| evt.msg.config_cmd.hdr.seid = seid; |
| evt.msg.config_cmd.hdr.ccb_idx = avdt_ccb_to_idx(p_ccb); |
| evt.msg.config_cmd.int_seid = handle; |
| evt.msg.config_cmd.p_cfg = p_cfg; |
| avdt_scb_event(p_scb, AVDT_SCB_API_SETCONFIG_REQ_EVT, &evt); |
| } |
| |
| AVDT_TRACE_DEBUG("%s: result=%d", __func__, result); |
| |
| return result; |
| } |
| |
| /******************************************************************************* |
| * |
| * Function AVDT_ConfigRsp |
| * |
| * Description Respond to a configure request from the peer device. This |
| * function must be called if the application receives an |
| * AVDT_CONFIG_IND_EVT through its control callback. |
| * |
| * |
| * Returns AVDT_SUCCESS if successful, otherwise error. |
| * |
| ******************************************************************************/ |
| uint16_t AVDT_ConfigRsp(uint8_t handle, uint8_t label, uint8_t error_code, |
| uint8_t category) { |
| tAVDT_SCB* p_scb; |
| tAVDT_SCB_EVT evt; |
| uint16_t result = AVDT_SUCCESS; |
| uint8_t event_code; |
| |
| AVDT_TRACE_DEBUG("%s: handle=%d label=%d error_code=%d category=%d", __func__, |
| handle, label, error_code, category); |
| |
| /* map handle to scb */ |
| p_scb = avdt_scb_by_hdl(handle); |
| if (p_scb == NULL) { |
| result = AVDT_BAD_HANDLE; |
| } |
| /* handle special case when this function is called but peer has not send |
| ** a configuration cmd; ignore and return error result |
| */ |
| else if (!p_scb->in_use) { |
| result = AVDT_BAD_HANDLE; |
| } |
| /* send event to scb */ |
| else { |
| evt.msg.hdr.err_code = error_code; |
| evt.msg.hdr.err_param = category; |
| evt.msg.hdr.label = label; |
| if (error_code == 0) { |
| event_code = AVDT_SCB_API_SETCONFIG_RSP_EVT; |
| } else { |
| event_code = AVDT_SCB_API_SETCONFIG_REJ_EVT; |
| } |
| avdt_scb_event(p_scb, event_code, &evt); |
| } |
| |
| AVDT_TRACE_DEBUG("%s: result=%d", __func__, result); |
| |
| return result; |
| } |
| |
| /******************************************************************************* |
| * |
| * Function AVDT_StartReq |
| * |
| * Description Start one or more stream endpoints. This initiates the |
| * transfer of media packets for the streams. All stream |
| * endpoints must previously be opened. When the streams |
| * are started, an AVDT_START_CFM_EVT is sent to the |
| * application via the control callback function for each |
| * stream. |
| * |
| * |
| * Returns AVDT_SUCCESS if successful, otherwise error. |
| * |
| ******************************************************************************/ |
| uint16_t AVDT_StartReq(uint8_t* p_handles, uint8_t num_handles) { |
| tAVDT_SCB* p_scb = NULL; |
| tAVDT_CCB_EVT evt; |
| uint16_t result = AVDT_SUCCESS; |
| int i; |
| |
| AVDT_TRACE_DEBUG("%s: num_handles=%d", __func__, num_handles); |
| |
| if ((num_handles == 0) || (num_handles > AVDT_NUM_SEPS)) { |
| result = AVDT_BAD_PARAMS; |
| } else { |
| /* verify handles */ |
| for (i = 0; i < num_handles; i++) { |
| p_scb = avdt_scb_by_hdl(p_handles[i]); |
| if (p_scb == NULL) { |
| result = AVDT_BAD_HANDLE; |
| break; |
| } |
| } |
| } |
| |
| if (result == AVDT_SUCCESS) { |
| if (p_scb->p_ccb == NULL) { |
| result = AVDT_BAD_HANDLE; |
| } else { |
| /* send event to ccb */ |
| memcpy(evt.msg.multi.seid_list, p_handles, num_handles); |
| evt.msg.multi.num_seps = num_handles; |
| avdt_ccb_event(p_scb->p_ccb, AVDT_CCB_API_START_REQ_EVT, &evt); |
| } |
| } |
| |
| AVDT_TRACE_DEBUG("%s: result=%d", __func__, result); |
| |
| return result; |
| } |
| |
| /******************************************************************************* |
| * |
| * Function AVDT_SuspendReq |
| * |
| * Description Suspend one or more stream endpoints. This suspends the |
| * transfer of media packets for the streams. All stream |
| * endpoints must previously be open and started. When the |
| * streams are suspended, an AVDT_SUSPEND_CFM_EVT is sent to |
| * the application via the control callback function for |
| * each stream. |
| * |
| * |
| * Returns AVDT_SUCCESS if successful, otherwise error. |
| * |
| ******************************************************************************/ |
| uint16_t AVDT_SuspendReq(uint8_t* p_handles, uint8_t num_handles) { |
| tAVDT_SCB* p_scb = NULL; |
| tAVDT_CCB_EVT evt; |
| uint16_t result = AVDT_SUCCESS; |
| int i; |
| |
| AVDT_TRACE_DEBUG("%s: num_handles=%d", __func__, num_handles); |
| |
| if ((num_handles == 0) || (num_handles > AVDT_NUM_SEPS)) { |
| result = AVDT_BAD_PARAMS; |
| } else { |
| /* verify handles */ |
| for (i = 0; i < num_handles; i++) { |
| p_scb = avdt_scb_by_hdl(p_handles[i]); |
| if (p_scb == NULL) { |
| result = AVDT_BAD_HANDLE; |
| break; |
| } |
| } |
| } |
| |
| if (result == AVDT_SUCCESS) { |
| if (p_scb->p_ccb == NULL) { |
| result = AVDT_BAD_HANDLE; |
| } else { |
| /* send event to ccb */ |
| memcpy(evt.msg.multi.seid_list, p_handles, num_handles); |
| evt.msg.multi.num_seps = num_handles; |
| avdt_ccb_event(p_scb->p_ccb, AVDT_CCB_API_SUSPEND_REQ_EVT, &evt); |
| } |
| } |
| |
| AVDT_TRACE_DEBUG("%s: result=%d", __func__, result); |
| |
| return result; |
| } |
| |
| /******************************************************************************* |
| * |
| * Function AVDT_CloseReq |
| * |
| * Description Close a stream endpoint. This stops the transfer of media |
| * packets and closes the transport channel associated with |
| * this stream endpoint. When the stream is closed, an |
| * AVDT_CLOSE_CFM_EVT is sent to the application via the |
| * control callback function for this handle. |
| * |
| * |
| * Returns AVDT_SUCCESS if successful, otherwise error. |
| * |
| ******************************************************************************/ |
| uint16_t AVDT_CloseReq(uint8_t handle) { |
| tAVDT_SCB* p_scb; |
| uint16_t result = AVDT_SUCCESS; |
| |
| AVDT_TRACE_DEBUG("%s: handle=%d", __func__, handle); |
| |
| /* map handle to scb */ |
| p_scb = avdt_scb_by_hdl(handle); |
| if (p_scb == NULL) { |
| result = AVDT_BAD_HANDLE; |
| } else |
| /* send event to scb */ |
| { |
| avdt_scb_event(p_scb, AVDT_SCB_API_CLOSE_REQ_EVT, NULL); |
| } |
| |
| AVDT_TRACE_DEBUG("%s: result=%d", __func__, result); |
| |
| return result; |
| } |
| |
| /******************************************************************************* |
| * |
| * Function AVDT_ReconfigReq |
| * |
| * Description Reconfigure a stream endpoint. This allows the application |
| * to change the codec or content protection capabilities of |
| * a stream endpoint after it has been opened. This function |
| * can only be called if the stream is opened but not started |
| * or if the stream has been suspended. When the procedure |
| * is completed, an AVDT_RECONFIG_CFM_EVT is sent to the |
| * application via the control callback function for this |
| * handle. |
| * |
| * |
| * Returns AVDT_SUCCESS if successful, otherwise error. |
| * |
| ******************************************************************************/ |
| uint16_t AVDT_ReconfigReq(uint8_t handle, tAVDT_CFG* p_cfg) { |
| tAVDT_SCB* p_scb; |
| uint16_t result = AVDT_SUCCESS; |
| tAVDT_SCB_EVT evt; |
| |
| AVDT_TRACE_DEBUG("%s: handle=%d", __func__, handle); |
| |
| /* map handle to scb */ |
| p_scb = avdt_scb_by_hdl(handle); |
| if (p_scb == NULL) { |
| result = AVDT_BAD_HANDLE; |
| } |
| /* send event to scb */ |
| else { |
| /* force psc_mask to zero */ |
| p_cfg->psc_mask = 0; |
| evt.msg.reconfig_cmd.p_cfg = p_cfg; |
| avdt_scb_event(p_scb, AVDT_SCB_API_RECONFIG_REQ_EVT, &evt); |
| } |
| |
| AVDT_TRACE_DEBUG("%s: result=%d", __func__, result); |
| |
| return result; |
| } |
| |
| /******************************************************************************* |
| * |
| * Function AVDT_ReconfigRsp |
| * |
| * Description Respond to a reconfigure request from the peer device. |
| * This function must be called if the application receives |
| * an AVDT_RECONFIG_IND_EVT through its control callback. |
| * |
| * |
| * Returns AVDT_SUCCESS if successful, otherwise error. |
| * |
| ******************************************************************************/ |
| uint16_t AVDT_ReconfigRsp(uint8_t handle, uint8_t label, uint8_t error_code, |
| uint8_t category) { |
| tAVDT_SCB* p_scb; |
| tAVDT_SCB_EVT evt; |
| uint16_t result = AVDT_SUCCESS; |
| |
| AVDT_TRACE_DEBUG("%s: handle=%d label=%d error_code=%d category=%d", __func__, |
| handle, label, error_code, category); |
| |
| /* map handle to scb */ |
| p_scb = avdt_scb_by_hdl(handle); |
| if (p_scb == NULL) { |
| result = AVDT_BAD_HANDLE; |
| } |
| /* send event to scb */ |
| else { |
| evt.msg.hdr.err_code = error_code; |
| evt.msg.hdr.err_param = category; |
| evt.msg.hdr.label = label; |
| avdt_scb_event(p_scb, AVDT_SCB_API_RECONFIG_RSP_EVT, &evt); |
| } |
| |
| AVDT_TRACE_DEBUG("%s: result=%d", __func__, result); |
| |
| return result; |
| } |
| |
| /******************************************************************************* |
| * |
| * Function AVDT_SecurityReq |
| * |
| * Description Send a security request to the peer device. When the |
| * security procedure is completed, an AVDT_SECURITY_CFM_EVT |
| * is sent to the application via the control callback function |
| * for this handle. (Please note that AVDTP security |
| * procedures are unrelated to Bluetooth link level security.) |
| * |
| * |
| * Returns AVDT_SUCCESS if successful, otherwise error. |
| * |
| ******************************************************************************/ |
| uint16_t AVDT_SecurityReq(uint8_t handle, uint8_t* p_data, uint16_t len) { |
| tAVDT_SCB* p_scb; |
| uint16_t result = AVDT_SUCCESS; |
| tAVDT_SCB_EVT evt; |
| |
| AVDT_TRACE_DEBUG("%s: handle=%d len=%d", __func__, handle, len); |
| |
| /* map handle to scb */ |
| p_scb = avdt_scb_by_hdl(handle); |
| if (p_scb == NULL) { |
| result = AVDT_BAD_HANDLE; |
| } |
| /* send event to scb */ |
| else { |
| evt.msg.security_rsp.p_data = p_data; |
| evt.msg.security_rsp.len = len; |
| avdt_scb_event(p_scb, AVDT_SCB_API_SECURITY_REQ_EVT, &evt); |
| } |
| |
| AVDT_TRACE_DEBUG("%s: result=%d", __func__, result); |
| |
| return result; |
| } |
| |
| /******************************************************************************* |
| * |
| * Function AVDT_SecurityRsp |
| * |
| * Description Respond to a security request from the peer device. |
| * This function must be called if the application receives |
| * an AVDT_SECURITY_IND_EVT through its control callback. |
| * (Please note that AVDTP security procedures are unrelated |
| * to Bluetooth link level security.) |
| * |
| * |
| * Returns AVDT_SUCCESS if successful, otherwise error. |
| * |
| ******************************************************************************/ |
| uint16_t AVDT_SecurityRsp(uint8_t handle, uint8_t label, uint8_t error_code, |
| uint8_t* p_data, uint16_t len) { |
| tAVDT_SCB* p_scb; |
| uint16_t result = AVDT_SUCCESS; |
| tAVDT_SCB_EVT evt; |
| |
| AVDT_TRACE_DEBUG("%s: handle=%d label=%d error_code=%d len=%d", __func__, |
| handle, label, error_code, len); |
| |
| /* map handle to scb */ |
| p_scb = avdt_scb_by_hdl(handle); |
| if (p_scb == NULL) { |
| result = AVDT_BAD_HANDLE; |
| } |
| /* send event to scb */ |
| else { |
| evt.msg.security_rsp.hdr.err_code = error_code; |
| evt.msg.security_rsp.hdr.label = label; |
| evt.msg.security_rsp.p_data = p_data; |
| evt.msg.security_rsp.len = len; |
| avdt_scb_event(p_scb, AVDT_SCB_API_SECURITY_RSP_EVT, &evt); |
| } |
| |
| AVDT_TRACE_DEBUG("%s: result=%d", __func__, result); |
| |
| return result; |
| } |
| |
| /******************************************************************************* |
| * |
| * Function AVDT_WriteReqOpt |
| * |
| * Description Send a media packet to the peer device. The stream must |
| * be started before this function is called. Also, this |
| * function can only be called if the stream is a SRC. |
| * |
| * When AVDTP has sent the media packet and is ready for the |
| * next packet, an AVDT_WRITE_CFM_EVT is sent to the |
| * application via the control callback. The application must |
| * wait for the AVDT_WRITE_CFM_EVT before it makes the next |
| * call to AVDT_WriteReq(). If the applications calls |
| * AVDT_WriteReq() before it receives the event the packet |
| * will not be sent. The application may make its first call |
| * to AVDT_WriteReq() after it receives an AVDT_START_CFM_EVT |
| * or AVDT_START_IND_EVT. |
| * |
| * The application passes the packet using the BT_HDR |
| * structure. |
| * This structure is described in section 2.1. The offset |
| * field must be equal to or greater than AVDT_MEDIA_OFFSET |
| * (if NO_RTP is specified, L2CAP_MIN_OFFSET can be used). |
| * This allows enough space in the buffer for the L2CAP and |
| * AVDTP headers. |
| * |
| * The memory pointed to by p_pkt must be a GKI buffer |
| * allocated by the application. This buffer will be freed |
| * by the protocol stack; the application must not free |
| * this buffer. |
| * |
| * The opt parameter allows passing specific options like: |
| * - NO_RTP : do not add the RTP header to buffer |
| * |
| * Returns AVDT_SUCCESS if successful, otherwise error. |
| * |
| ******************************************************************************/ |
| uint16_t AVDT_WriteReqOpt(uint8_t handle, BT_HDR* p_pkt, uint32_t time_stamp, |
| uint8_t m_pt, tAVDT_DATA_OPT_MASK opt) { |
| tAVDT_SCB* p_scb; |
| tAVDT_SCB_EVT evt; |
| uint16_t result = AVDT_SUCCESS; |
| |
| AVDT_TRACE_DEBUG("%s: handle=%d timestamp=%d m_pt=0x%x opt=0x%x", __func__, |
| handle, time_stamp, m_pt, opt); |
| |
| /* map handle to scb */ |
| p_scb = avdt_scb_by_hdl(handle); |
| if (p_scb == NULL) { |
| result = AVDT_BAD_HANDLE; |
| } else { |
| evt.apiwrite.p_buf = p_pkt; |
| evt.apiwrite.time_stamp = time_stamp; |
| evt.apiwrite.m_pt = m_pt; |
| evt.apiwrite.opt = opt; |
| avdt_scb_event(p_scb, AVDT_SCB_API_WRITE_REQ_EVT, &evt); |
| } |
| |
| AVDT_TRACE_DEBUG("%s: result=%d", __func__, result); |
| |
| return result; |
| } |
| |
| /******************************************************************************* |
| * |
| * Function AVDT_WriteReq |
| * |
| * Description Send a media packet to the peer device. The stream must |
| * be started before this function is called. Also, this |
| * function can only be called if the stream is a SRC. |
| * |
| * When AVDTP has sent the media packet and is ready for the |
| * next packet, an AVDT_WRITE_CFM_EVT is sent to the |
| * application via the control callback. The application must |
| * wait for the AVDT_WRITE_CFM_EVT before it makes the next |
| * call to AVDT_WriteReq(). If the applications calls |
| * AVDT_WriteReq() before it receives the event the packet |
| * will not be sent. The application may make its first call |
| * to AVDT_WriteReq() after it receives an AVDT_START_CFM_EVT |
| * or AVDT_START_IND_EVT. |
| * |
| * The application passes the packet using the BT_HDR |
| * structure. |
| * This structure is described in section 2.1. The offset |
| * field must be equal to or greater than AVDT_MEDIA_OFFSET. |
| * This allows enough space in the buffer for the L2CAP and |
| * AVDTP headers. |
| * |
| * The memory pointed to by p_pkt must be a GKI buffer |
| * allocated by the application. This buffer will be freed |
| * by the protocol stack; the application must not free |
| * this buffer. |
| * |
| * |
| * Returns AVDT_SUCCESS if successful, otherwise error. |
| * |
| ******************************************************************************/ |
| uint16_t AVDT_WriteReq(uint8_t handle, BT_HDR* p_pkt, uint32_t time_stamp, |
| uint8_t m_pt) { |
| return AVDT_WriteReqOpt(handle, p_pkt, time_stamp, m_pt, AVDT_DATA_OPT_NONE); |
| } |
| |
| /******************************************************************************* |
| * |
| * Function AVDT_ConnectReq |
| * |
| * Description This function initiates an AVDTP signaling connection |
| * to the peer device. When the connection is completed, an |
| * AVDT_CONNECT_IND_EVT is sent to the application via its |
| * control callback function. If the connection attempt fails |
| * an AVDT_DISCONNECT_IND_EVT is sent. The security mask |
| * parameter overrides the outgoing security mask set in |
| * AVDT_Register(). |
| * |
| * Returns AVDT_SUCCESS if successful, otherwise error. |
| * |
| ******************************************************************************/ |
| uint16_t AVDT_ConnectReq(const RawAddress& bd_addr, uint8_t sec_mask, |
| tAVDT_CTRL_CBACK* p_cback) { |
| tAVDT_CCB* p_ccb = NULL; |
| uint16_t result = AVDT_SUCCESS; |
| tAVDT_CCB_EVT evt; |
| |
| AVDT_TRACE_DEBUG("%s: sec_mask=0x%x", __func__, sec_mask); |
| |
| /* find channel control block for this bd addr; if none, allocate one */ |
| p_ccb = avdt_ccb_by_bd(bd_addr); |
| if (p_ccb == NULL) { |
| p_ccb = avdt_ccb_alloc(bd_addr); |
| if (p_ccb == NULL) { |
| /* could not allocate channel control block */ |
| result = AVDT_NO_RESOURCES; |
| } |
| } else if (!p_ccb->ll_opened) { |
| AVDT_TRACE_WARNING("AVDT_ConnectReq: CCB LL is in the middle of opening"); |
| |
| /* ccb was already allocated for the incoming signalling. */ |
| result = AVDT_BUSY; |
| } |
| |
| if (result == AVDT_SUCCESS) { |
| /* send event to ccb */ |
| evt.connect.p_cback = p_cback; |
| evt.connect.sec_mask = sec_mask; |
| avdt_ccb_event(p_ccb, AVDT_CCB_API_CONNECT_REQ_EVT, &evt); |
| } |
| |
| AVDT_TRACE_DEBUG("%s: result=%d", __func__, result); |
| |
| return result; |
| } |
| |
| /******************************************************************************* |
| * |
| * Function AVDT_DisconnectReq |
| * |
| * Description This function disconnect an AVDTP signaling connection |
| * to the peer device. When disconnected an |
| * AVDT_DISCONNECT_IND_EVT is sent to the application via its |
| * control callback function. |
| * |
| * Returns AVDT_SUCCESS if successful, otherwise error. |
| * |
| ******************************************************************************/ |
| uint16_t AVDT_DisconnectReq(const RawAddress& bd_addr, |
| tAVDT_CTRL_CBACK* p_cback) { |
| tAVDT_CCB* p_ccb = NULL; |
| uint16_t result = AVDT_SUCCESS; |
| tAVDT_CCB_EVT evt; |
| |
| AVDT_TRACE_WARNING("%s: address=%s", __func__, bd_addr.ToString().c_str()); |
| |
| /* find channel control block for this bd addr; if none, error */ |
| p_ccb = avdt_ccb_by_bd(bd_addr); |
| if (p_ccb == NULL) { |
| result = AVDT_BAD_PARAMS; |
| } |
| |
| if (result == AVDT_SUCCESS) { |
| /* send event to ccb */ |
| evt.disconnect.p_cback = p_cback; |
| avdt_ccb_event(p_ccb, AVDT_CCB_API_DISCONNECT_REQ_EVT, &evt); |
| } |
| |
| AVDT_TRACE_DEBUG("%s: address=%s result=%d", __func__, |
| bd_addr.ToString().c_str(), result); |
| |
| return result; |
| } |
| |
| /******************************************************************************* |
| * |
| * Function AVDT_GetL2CapChannel |
| * |
| * Description Get the L2CAP CID used by the handle. |
| * |
| * Returns CID if successful, otherwise 0. |
| * |
| ******************************************************************************/ |
| uint16_t AVDT_GetL2CapChannel(uint8_t handle) { |
| tAVDT_SCB* p_scb; |
| tAVDT_CCB* p_ccb; |
| uint8_t tcid; |
| uint16_t lcid = 0; |
| |
| /* map handle to scb */ |
| if (((p_scb = avdt_scb_by_hdl(handle)) != NULL) && |
| ((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; |
| } |
| |
| return (lcid); |
| } |
| |
| /******************************************************************************* |
| * |
| * Function AVDT_GetSignalChannel |
| * |
| * Description Get the L2CAP CID used by the signal channel of the given |
| * handle. |
| * |
| * Returns CID if successful, otherwise 0. |
| * |
| ******************************************************************************/ |
| uint16_t AVDT_GetSignalChannel(uint8_t handle, const RawAddress& bd_addr) { |
| tAVDT_SCB* p_scb; |
| tAVDT_CCB* p_ccb; |
| uint8_t tcid = 0; /* tcid is always 0 for signal channel */ |
| uint16_t lcid = 0; |
| |
| /* map handle to scb */ |
| if (((p_scb = avdt_scb_by_hdl(handle)) != NULL) && |
| ((p_ccb = p_scb->p_ccb) != NULL)) { |
| lcid = avdt_cb.ad.rt_tbl[avdt_ccb_to_idx(p_ccb)][tcid].lcid; |
| } else { |
| p_ccb = avdt_ccb_by_bd(bd_addr); |
| if (p_ccb != NULL) { |
| lcid = avdt_cb.ad.rt_tbl[avdt_ccb_to_idx(p_ccb)][tcid].lcid; |
| } |
| } |
| |
| return (lcid); |
| } |
| |
| #if (AVDT_REPORTING == TRUE) |
| /******************************************************************************* |
| * |
| * Function AVDT_SendReport |
| * |
| * Description |
| * |
| * |
| * |
| * Returns |
| * |
| ******************************************************************************/ |
| uint16_t AVDT_SendReport(uint8_t handle, AVDT_REPORT_TYPE type, |
| tAVDT_REPORT_DATA* p_data) { |
| tAVDT_SCB* p_scb; |
| uint16_t result = AVDT_BAD_PARAMS; |
| tAVDT_TC_TBL* p_tbl; |
| uint8_t *p, *plen, *pm1, *p_end; |
| uint32_t ssrc; |
| uint16_t len; |
| |
| AVDT_TRACE_DEBUG("%s: handle=%d type=%d", __func__, handle, type); |
| |
| /* map handle to scb && verify parameters */ |
| if (((p_scb = avdt_scb_by_hdl(handle)) != NULL) && (p_scb->p_ccb != NULL) && |
| (((type == AVDT_RTCP_PT_SR) && (p_scb->cs.tsep == AVDT_TSEP_SRC)) || |
| ((type == AVDT_RTCP_PT_RR) && (p_scb->cs.tsep == AVDT_TSEP_SNK)) || |
| (type == AVDT_RTCP_PT_SDES))) { |
| result = AVDT_NO_RESOURCES; |
| |
| /* build SR - assume fit in one packet */ |
| p_tbl = avdt_ad_tc_tbl_by_type(AVDT_CHAN_REPORT, p_scb->p_ccb, p_scb); |
| if (p_tbl->state == AVDT_AD_ST_OPEN) { |
| BT_HDR* p_pkt = (BT_HDR*)osi_malloc(p_tbl->peer_mtu); |
| |
| p_pkt->offset = L2CAP_MIN_OFFSET; |
| p = (uint8_t*)(p_pkt + 1) + p_pkt->offset; |
| pm1 = p; |
| *p++ = AVDT_MEDIA_OCTET1 | 1; |
| *p++ = type; |
| /* save the location for length */ |
| plen = p; |
| p += 2; |
| ssrc = avdt_scb_gen_ssrc(p_scb); |
| UINT32_TO_BE_STREAM(p, ssrc); |
| |
| switch (type) { |
| case AVDT_RTCP_PT_SR: /* Sender Report */ |
| *pm1 = AVDT_MEDIA_OCTET1; |
| UINT32_TO_BE_STREAM(p, p_data->sr.ntp_sec); |
| UINT32_TO_BE_STREAM(p, p_data->sr.ntp_frac); |
| UINT32_TO_BE_STREAM(p, p_data->sr.rtp_time); |
| UINT32_TO_BE_STREAM(p, p_data->sr.pkt_count); |
| UINT32_TO_BE_STREAM(p, p_data->sr.octet_count); |
| break; |
| |
| case AVDT_RTCP_PT_RR: /* Receiver Report */ |
| *p++ = p_data->rr.frag_lost; |
| AVDT_TRACE_API("packet_lost: %d", p_data->rr.packet_lost); |
| p_data->rr.packet_lost &= 0xFFFFFF; |
| AVDT_TRACE_API("packet_lost: %d", p_data->rr.packet_lost); |
| UINT24_TO_BE_STREAM(p, p_data->rr.packet_lost); |
| UINT32_TO_BE_STREAM(p, p_data->rr.seq_num_rcvd); |
| UINT32_TO_BE_STREAM(p, p_data->rr.jitter); |
| UINT32_TO_BE_STREAM(p, p_data->rr.lsr); |
| UINT32_TO_BE_STREAM(p, p_data->rr.dlsr); |
| break; |
| |
| case AVDT_RTCP_PT_SDES: /* Source Description */ |
| *p++ = AVDT_RTCP_SDES_CNAME; |
| len = strlen((char*)p_data->cname); |
| if (len > AVDT_MAX_CNAME_SIZE) len = AVDT_MAX_CNAME_SIZE; |
| *p++ = (uint8_t)len; |
| strlcpy((char*)p, (char*)p_data->cname, len + 1); |
| p += len; |
| break; |
| } |
| p_end = p; |
| len = p - pm1 - 1; |
| UINT16_TO_BE_STREAM(plen, len); |
| |
| /* set the actual payload length */ |
| p_pkt->len = p_end - p; |
| /* send the packet */ |
| if (L2CAP_DW_FAILED != |
| avdt_ad_write_req(AVDT_CHAN_REPORT, p_scb->p_ccb, p_scb, p_pkt)) |
| result = AVDT_SUCCESS; |
| } |
| } |
| |
| AVDT_TRACE_DEBUG("%s: result=%d", __func__, result); |
| |
| return result; |
| } |
| #endif |
| |
| /****************************************************************************** |
| * |
| * Function AVDT_SetTraceLevel |
| * |
| * Description Sets the trace level for AVDT. If 0xff is passed, the |
| * current trace level is returned. |
| * |
| * Input Parameters: |
| * new_level: The level to set the AVDT tracing to: |
| * 0xff-returns the current setting. |
| * 0-turns off tracing. |
| * >= 1-Errors. |
| * >= 2-Warnings. |
| * >= 3-APIs. |
| * >= 4-Events. |
| * >= 5-Debug. |
| * |
| * Returns The new trace level or current trace level if |
| * the input parameter is 0xff. |
| * |
| *****************************************************************************/ |
| uint8_t AVDT_SetTraceLevel(uint8_t new_level) { |
| if (new_level != 0xFF) avdt_cb.trace_level = new_level; |
| |
| return (avdt_cb.trace_level); |
| } |