--- /dev/null
- esp_err_t esp_a2d_register_callback(esp_profile_cb_t callback);
+// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
+//
+// 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.
+
+#ifndef __ESP_A2DP_API_H__
+#define __ESP_A2DP_API_H__
+
+#include "esp_err.h"
+#include "esp_bt_defs.h"
+
+/// Media codec types supported by A2DP
+#define ESP_A2D_MCT_SBC (0) /*!< SBC */
+#define ESP_A2D_MCT_M12 (0x01) /*!< MPEG-1, 2 Audio */
+#define ESP_A2D_MCT_M24 (0x02) /*!< MPEG-2, 4 AAC */
+#define ESP_A2D_MCT_ATRAC (0x04) /*!< ATRAC family */
+#define ESP_A2D_MCT_NON_A2DP (0xff)
+typedef uint8_t esp_a2d_mct_t;
+
+/**
+ * @brief Codec specific information elements as defined in A2DP spec.
+ */
+
+/**
+ * @brief SBC codec specific information
+ */
+typedef struct {
+#define ESP_A2D_CIE_LEN_SBC (4)
+ uint8_t oct[ESP_A2D_CIE_LEN_SBC];
+} esp_a2d_cie_sbc_t;
+
+/**
+ * @brief MPEG-1,2 Audio codec specific information
+ */
+typedef struct {
+#define ESP_A2D_CIE_LEN_M12 (4)
+ uint8_t oct[ESP_A2D_CIE_LEN_M12];
+} esp_a2d_cie_m12_t;
+
+/**
+ * @brief MPEG-2,4 AAC codec specific information
+ */
+typedef struct {
+#define ESP_A2D_CIE_LEN_M24 (6)
+ uint8_t oct[ESP_A2D_CIE_LEN_M24];
+} esp_a2d_cie_m24_t;
+
+/**
+ * @brief ATRAC family codec specific information
+ */
+typedef struct {
+#define ESP_A2D_CIE_LEN_ATRAC (7)
+ uint8_t oct[ESP_A2D_CIE_LEN_ATRAC];
+} esp_a2d_cie_atrac_t;
+
+/**
+ * @brief A2DP media codec capabilities union
+ */
+typedef struct {
+ esp_a2d_mct_t type; /*!< A2DP media codec type */
+ union {
+ esp_a2d_cie_sbc_t sbc;
+ esp_a2d_cie_m12_t m12;
+ esp_a2d_cie_m24_t m24;
+ esp_a2d_cie_atrac_t atrac;
+ } cie;
+} esp_a2d_mcc_t;
+
+/// Bluetooth A2DP connection states
+typedef enum {
+ ESP_A2D_CONNECTION_STATE_DISCONNECTED = 0,
+ ESP_A2D_CONNECTION_STATE_CONNECTING,
+ ESP_A2D_CONNECTION_STATE_CONNECTED,
+ ESP_A2D_CONNECTION_STATE_DISCONNECTING
+} esp_a2d_connection_state_t;
+
+/// Bluetooth A2DP datapath states
+typedef enum {
+ ESP_A2D_AUDIO_STATE_REMOTE_SUSPEND = 0,
+ ESP_A2D_AUDIO_STATE_STOPPED,
+ ESP_A2D_AUDIO_STATE_STARTED,
+} esp_a2d_audio_state_t;
+
+/// A2DP callback events
+typedef enum {
+ ESP_A2D_CONNECTION_STATE_EVT = 0, /*!< connection state changed event */
+ ESP_A2D_AUDIO_STATE_EVT = 1, /*!< audio stream transmission state changed event */
+ ESP_A2D_AUDIO_CFG_EVT = 2 /*!< audio codec configuration received */
+} esp_a2d_cb_event_t;
+
+/// A2DP state callback parameters
+typedef union {
+ /*< ESP_A2D_CONNECTION_STATE_EVT */
+ struct a2d_conn_stat_param {
+ esp_a2d_connection_state_t state; /*!< one of values from esp_a2d_connection_state_t */
+ esp_bd_addr_t remote_bda;
+ } conn_stat;
+
+ /*< ESP_A2D_AUDIO_STATE_EVT */
+ struct a2d_audio_stat_param {
+ esp_a2d_audio_state_t state; /*!< one of the values from esp_a2d_audio_state_t */
+ esp_bd_addr_t remote_bda;
+ } audio_stat;
+
+ /*< ESP_A2D_AUDIO_CFG_EVT */
+ struct a2d_audio_cfg_param {
+ esp_bd_addr_t remote_bda;
+ esp_a2d_mcc_t mcc; /*!< A2DP media codec capability information */
+ } audio_cfg;
+} esp_a2d_cb_param_t;
+
++/**
++ * @brief A2DP profile callback function, data is ou
++ * @param event : Event type
++ * @param param : Point to callback parameter
++ */
++typedef void (* esp_a2d_cb_t)(esp_a2d_cb_event_t event, esp_a2d_cb_param_t *param);
++
+/**
+ * @brief A2DP profile data callback function, data is ou
+ *
+ * @param[in] buf: data received and decoded to PCM format, buf points to a static memory
+ * and can be overwritten in later times
+ * @param[in] len: size(in bytes) in buf
+ *
+ */
+typedef void (* esp_a2d_data_cb_t)(const uint8_t *buf, uint32_t len);
+
+
+/**
+ * @brief This function is called to register application callbacks
+ * with A2DP module; callbacks will provide the upstream events
+ * (type esp_a2d_cb_event_t) and paramters(type esp_a2d_cb_param_t)
+ *
+ * @param[in] callback: A2DP sink event callback function
+ *
+ * @return
+ * - ESP_OK: success
+ *
+ */
++esp_err_t esp_a2d_register_callback(esp_a2d_cb_t callback);
+
+
+/**
+ * @brief This function is called to register A2DP sink data output function
+ * for now only supports SBC codec, and the output is PCM data stream
+ *
+ * @param[in] callback: A2DP data callback function
+ *
+ * @return
+ * - ESP_OK: success
+ *
+ */
+esp_err_t esp_a2d_register_data_callback(esp_a2d_data_cb_t cb);
+
+
+/**
+ *
+ * @brief This function is called to initialize the bluetooth A2DP sink module
+ *
+ * @return
+ * - ESP_OK: success
+ * - ESP_FAIL: others
+ *
+ */
+esp_err_t esp_a2d_sink_init(void);
+
+
+/**
+ *
+ * @brief This function is called to deinit and free the resources for A2DP sink module
+ *
+ */
+void esp_a2d_sink_deinit(void);
+
+
+/**
+ *
+ * @brief This function is called to disconnect with the remote bluetooth device
+ *
+ * @return
+ * - ESP_OK: disconnect request is sent to lower layer
+ * - ESP_FAIL: others
+ *
+ */
+esp_err_t esp_a2d_sink_disconnect(esp_bd_addr_t remote_bda);
+
+#endif /* __ESP_A2DP_API_H__ */
--- /dev/null
- static esp_profile_cb_t bt_av_sink_callback = NULL;
+/******************************************************************************
+ *
+ * Copyright (C) 2009-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.
+ *
+ ******************************************************************************/
+
+
+/*****************************************************************************
+ *
+ * Filename: btif_av.c
+ *
+ * Description: Bluedroid AV implementation
+ *
+ *****************************************************************************/
+
+#include "bt_trace.h"
+#include <string.h>
+
+#include "bt_defs.h"
+#include "esp_bt_defs.h"
+#include "esp_a2dp_api.h"
+#include "allocator.h"
+
+#define LOG_TAG "bt_btif_av"
+
+#include "btif_av.h"
+#include "btif_util.h"
+#include "btif_profile_queue.h"
+#include "bta_api.h"
+#include "btif_media.h"
+#include "bta_av_api.h"
+#include "gki.h"
+#include "btu.h"
+#include "bt_utils.h"
+
+/*****************************************************************************
+** Constants & Macros
+******************************************************************************/
+#define BTIF_AV_SERVICE_NAME "Advanced Audio"
+
+#define BTIF_TIMEOUT_AV_OPEN_ON_RC_SECS 2
+
+typedef enum {
+ BTIF_AV_STATE_IDLE = 0x0,
+ BTIF_AV_STATE_OPENING,
+ BTIF_AV_STATE_OPENED,
+ BTIF_AV_STATE_STARTED,
+ BTIF_AV_STATE_CLOSING
+} btif_av_state_t;
+
+/* Should not need dedicated suspend state as actual actions are no
+ different than open state. Suspend flags are needed however to prevent
+ media task from trying to restart stream during remote suspend or while
+ we are in the process of a local suspend */
+
+#define BTIF_AV_FLAG_LOCAL_SUSPEND_PENDING 0x1
+#define BTIF_AV_FLAG_REMOTE_SUSPEND 0x2
+#define BTIF_AV_FLAG_PENDING_START 0x4
+#define BTIF_AV_FLAG_PENDING_STOP 0x8
+
+/*****************************************************************************
+** Local type definitions
+******************************************************************************/
+
+typedef struct {
+ tBTA_AV_HNDL bta_handle;
+ bt_bdaddr_t peer_bda;
+ btif_sm_handle_t sm_handle;
+ UINT8 flags;
+ tBTA_AV_EDR edr;
+ UINT8 peer_sep; /* sep type of peer device */
+} btif_av_cb_t;
+
+typedef struct {
+ bt_bdaddr_t *target_bda;
+ uint16_t uuid;
+} btif_av_connect_req_t;
+
+/*****************************************************************************
+** Static variables
+******************************************************************************/
+
- esp_err_t esp_a2d_register_callback(esp_profile_cb_t callback)
++static esp_a2d_cb_t bt_av_sink_callback = NULL;
+
+static btif_av_cb_t btif_av_cb = {0};
+static TIMER_LIST_ENT tle_av_open_on_rc;
+
+// TODO: need protection against race
+#define BTIF_A2D_CB_TO_APP(_event, _param) do { \
+ if (bt_av_sink_callback) { \
+ bt_av_sink_callback(_event, _param); \
+ } \
+ } while (0)
+
+/* both interface and media task needs to be ready to alloc incoming request */
+#define CHECK_BTAV_INIT() if (btif_av_cb.sm_handle == NULL)\
+{\
+ BTIF_TRACE_WARNING("%s: BTAV not initialized\n", __FUNCTION__);\
+ return ESP_ERR_INVALID_STATE;\
+}\
+else\
+{\
+ BTIF_TRACE_EVENT("%s\n", __FUNCTION__);\
+}
+
+/* Helper macro to avoid code duplication in the state machine handlers */
+#define CHECK_RC_EVENT(e, d) \
+ case BTA_AV_RC_OPEN_EVT: \
+ case BTA_AV_RC_CLOSE_EVT: \
+ case BTA_AV_REMOTE_CMD_EVT: \
+ case BTA_AV_VENDOR_CMD_EVT: \
+ case BTA_AV_META_MSG_EVT: \
+ case BTA_AV_RC_FEAT_EVT: \
+ case BTA_AV_REMOTE_RSP_EVT: \
+ { \
+ btif_rc_handler(e, d);\
+ }break; \
+
+static BOOLEAN btif_av_state_idle_handler(btif_sm_event_t event, void *data);
+static BOOLEAN btif_av_state_opening_handler(btif_sm_event_t event, void *data);
+static BOOLEAN btif_av_state_opened_handler(btif_sm_event_t event, void *data);
+static BOOLEAN btif_av_state_started_handler(btif_sm_event_t event, void *data);
+static BOOLEAN btif_av_state_closing_handler(btif_sm_event_t event, void *data);
+
+static const btif_sm_handler_t btif_av_state_handlers[] = {
+ btif_av_state_idle_handler,
+ btif_av_state_opening_handler,
+ btif_av_state_opened_handler,
+ btif_av_state_started_handler,
+ btif_av_state_closing_handler
+};
+
+static void btif_av_event_free_data(btif_sm_event_t event, void *p_data);
+
+/*************************************************************************
+** Extern functions
+*************************************************************************/
+extern void btif_rc_handler(tBTA_AV_EVT event, tBTA_AV *p_data);
+extern BOOLEAN btif_rc_get_connected_peer(BD_ADDR peer_addr);
+extern void btif_rc_check_handle_pending_play (BD_ADDR peer_addr, BOOLEAN bSendToApp);
+
+extern tBTA_AV_CO_FUNCTS bta_av_a2d_cos;
+/*****************************************************************************
+** Local helper functions
+******************************************************************************/
+
+const char *dump_av_sm_state_name(btif_av_state_t state)
+{
+ switch (state) {
+ CASE_RETURN_STR(BTIF_AV_STATE_IDLE)
+ CASE_RETURN_STR(BTIF_AV_STATE_OPENING)
+ CASE_RETURN_STR(BTIF_AV_STATE_OPENED)
+ CASE_RETURN_STR(BTIF_AV_STATE_STARTED)
+ CASE_RETURN_STR(BTIF_AV_STATE_CLOSING)
+ default: return "UNKNOWN_STATE";
+ }
+}
+
+const char *dump_av_sm_event_name(btif_av_sm_event_t event)
+{
+ switch ((int)event) {
+ CASE_RETURN_STR(BTA_AV_ENABLE_EVT)
+ CASE_RETURN_STR(BTA_AV_REGISTER_EVT)
+ CASE_RETURN_STR(BTA_AV_OPEN_EVT)
+ CASE_RETURN_STR(BTA_AV_CLOSE_EVT)
+ CASE_RETURN_STR(BTA_AV_START_EVT)
+ CASE_RETURN_STR(BTA_AV_STOP_EVT)
+ CASE_RETURN_STR(BTA_AV_PROTECT_REQ_EVT)
+ CASE_RETURN_STR(BTA_AV_PROTECT_RSP_EVT)
+ CASE_RETURN_STR(BTA_AV_RC_OPEN_EVT)
+ CASE_RETURN_STR(BTA_AV_RC_CLOSE_EVT)
+ CASE_RETURN_STR(BTA_AV_REMOTE_CMD_EVT)
+ CASE_RETURN_STR(BTA_AV_REMOTE_RSP_EVT)
+ CASE_RETURN_STR(BTA_AV_VENDOR_CMD_EVT)
+ CASE_RETURN_STR(BTA_AV_VENDOR_RSP_EVT)
+ CASE_RETURN_STR(BTA_AV_RECONFIG_EVT)
+ CASE_RETURN_STR(BTA_AV_SUSPEND_EVT)
+ CASE_RETURN_STR(BTA_AV_PENDING_EVT)
+ CASE_RETURN_STR(BTA_AV_META_MSG_EVT)
+ CASE_RETURN_STR(BTA_AV_REJECT_EVT)
+ CASE_RETURN_STR(BTA_AV_RC_FEAT_EVT)
+ CASE_RETURN_STR(BTIF_SM_ENTER_EVT)
+ CASE_RETURN_STR(BTIF_SM_EXIT_EVT)
+ CASE_RETURN_STR(BTIF_AV_CONNECT_REQ_EVT)
+ CASE_RETURN_STR(BTIF_AV_DISCONNECT_REQ_EVT)
+ CASE_RETURN_STR(BTIF_AV_START_STREAM_REQ_EVT)
+ CASE_RETURN_STR(BTIF_AV_STOP_STREAM_REQ_EVT)
+ CASE_RETURN_STR(BTIF_AV_SUSPEND_STREAM_REQ_EVT)
+ CASE_RETURN_STR(BTIF_AV_SINK_CONFIG_REQ_EVT)
+ default: return "UNKNOWN_EVENT";
+ }
+}
+
+/****************************************************************************
+** Local helper functions
+*****************************************************************************/
+/*******************************************************************************
+**
+** Function btif_initiate_av_open_tmr_hdlr
+**
+** Description Timer to trigger AV open if the remote headset establishes
+** RC connection w/o AV connection. The timer is needed to IOP
+** with headsets that do establish AV after RC connection.
+**
+** Returns void
+**
+*******************************************************************************/
+static void btif_initiate_av_open_tmr_hdlr(TIMER_LIST_ENT *tle)
+{
+ BD_ADDR peer_addr;
+ UNUSED(tle);
+ btif_av_connect_req_t connect_req;
+ UNUSED(tle);
+ /* is there at least one RC connection - There should be */
+ if (btif_rc_get_connected_peer(peer_addr)) {
+ BTIF_TRACE_DEBUG("%s Issuing connect to the remote RC peer", __FUNCTION__);
+ /* In case of AVRCP connection request, we will initiate SRC connection */
+ connect_req.target_bda = (bt_bdaddr_t *)&peer_addr;
+ connect_req.uuid = UUID_SERVCLASS_AUDIO_SOURCE;
+ btif_sm_dispatch(btif_av_cb.sm_handle, BTIF_AV_CONNECT_REQ_EVT, (char *)&connect_req);
+ } else {
+ BTIF_TRACE_ERROR("%s No connected RC peers", __FUNCTION__);
+ }
+}
+
+/*****************************************************************************
+** Static functions
+******************************************************************************/
+static void btif_report_connection_state(esp_a2d_connection_state_t state, bt_bdaddr_t *bd_addr)
+{
+ esp_a2d_cb_param_t param;
+ memset(¶m, 0, sizeof(esp_a2d_cb_param_t));
+
+ param.conn_stat.state = state;
+ if (bd_addr) {
+ memcpy(param.conn_stat.remote_bda, bd_addr, sizeof(esp_bd_addr_t));
+ }
+ BTIF_A2D_CB_TO_APP(ESP_A2D_CONNECTION_STATE_EVT, ¶m);
+}
+
+static void btif_report_audio_state(esp_a2d_audio_state_t state, bt_bdaddr_t *bd_addr)
+{
+ esp_a2d_cb_param_t param;
+ memset(¶m, 0, sizeof(esp_a2d_cb_param_t));
+
+ param.audio_stat.state = state;
+ if (bd_addr) {
+ memcpy(param.audio_stat.remote_bda, bd_addr, sizeof(esp_bd_addr_t));
+ }
+ BTIF_A2D_CB_TO_APP(ESP_A2D_AUDIO_STATE_EVT, ¶m);
+}
+
+/*****************************************************************************
+**
+** Function btif_av_state_idle_handler
+**
+** Description State managing disconnected AV link
+**
+** Returns TRUE if event was processed, FALSE otherwise
+**
+*******************************************************************************/
+
+static BOOLEAN btif_av_state_idle_handler(btif_sm_event_t event, void *p_data)
+{
+ BTIF_TRACE_DEBUG("%s event:%s flags %x\n", __FUNCTION__,
+ dump_av_sm_event_name(event), btif_av_cb.flags);
+
+ switch (event) {
+ case BTIF_SM_ENTER_EVT:
+ /* clear the peer_bda */
+ memset(&btif_av_cb.peer_bda, 0, sizeof(bt_bdaddr_t));
+ btif_av_cb.flags = 0;
+ btif_av_cb.edr = 0;
+ btif_a2dp_on_idle();
+ break;
+
+ case BTIF_SM_EXIT_EVT:
+ break;
+
+ case BTA_AV_ENABLE_EVT:
+ break;
+
+ case BTA_AV_REGISTER_EVT:
+ btif_av_cb.bta_handle = ((tBTA_AV *)p_data)->registr.hndl;
+ break;
+
+ case BTA_AV_PENDING_EVT:
+ case BTIF_AV_CONNECT_REQ_EVT: {
+ if (event == BTIF_AV_CONNECT_REQ_EVT) {
+ memcpy(&btif_av_cb.peer_bda, ((btif_av_connect_req_t *)p_data)->target_bda,
+ sizeof(bt_bdaddr_t));
+ BTA_AvOpen(btif_av_cb.peer_bda.address, btif_av_cb.bta_handle,
+ TRUE, BTA_SEC_AUTHENTICATE, ((btif_av_connect_req_t *)p_data)->uuid);
+ } else if (event == BTA_AV_PENDING_EVT) {
+ bdcpy(btif_av_cb.peer_bda.address, ((tBTA_AV *)p_data)->pend.bd_addr);
+ BTA_AvOpen(btif_av_cb.peer_bda.address, btif_av_cb.bta_handle,
+ TRUE, BTA_SEC_AUTHENTICATE, UUID_SERVCLASS_AUDIO_SOURCE);
+ }
+ btif_sm_change_state(btif_av_cb.sm_handle, BTIF_AV_STATE_OPENING);
+ } break;
+
+ case BTA_AV_RC_OPEN_EVT:
+ /* IOP_FIX: Jabra 620 only does RC open without AV open whenever it connects. So
+ * as per the AV WP, an AVRC connection cannot exist without an AV connection. Therefore,
+ * we initiate an AV connection if an RC_OPEN_EVT is received when we are in AV_CLOSED state.
+ * We initiate the AV connection after a small 3s timeout to avoid any collisions from the
+ * headsets, as some headsets initiate the AVRC connection first and then
+ * immediately initiate the AV connection
+ *
+ * TODO: We may need to do this only on an AVRCP Play. FixMe
+ */
+
+ BTIF_TRACE_DEBUG("BTA_AV_RC_OPEN_EVT received w/o AV");
+ memset(&tle_av_open_on_rc, 0, sizeof(tle_av_open_on_rc));
+ tle_av_open_on_rc.param = (UINT32)btif_initiate_av_open_tmr_hdlr;
+ btu_start_timer(&tle_av_open_on_rc, BTU_TTYPE_USER_FUNC,
+ BTIF_TIMEOUT_AV_OPEN_ON_RC_SECS);
+ btif_rc_handler(event, p_data);
+ break;
+
+ case BTA_AV_REMOTE_CMD_EVT:
+ case BTA_AV_VENDOR_CMD_EVT:
+ case BTA_AV_META_MSG_EVT:
+ case BTA_AV_RC_FEAT_EVT:
+ case BTA_AV_REMOTE_RSP_EVT:
+ btif_rc_handler(event, (tBTA_AV *)p_data);
+ break;
+
+ case BTA_AV_RC_CLOSE_EVT:
+ if (tle_av_open_on_rc.in_use) {
+ BTIF_TRACE_DEBUG("BTA_AV_RC_CLOSE_EVT: Stopping AV timer.");
+ btu_stop_timer(&tle_av_open_on_rc);
+ }
+ btif_rc_handler(event, p_data);
+ break;
+
+ default:
+ BTIF_TRACE_WARNING("%s : unhandled event:%s\n", __FUNCTION__,
+ dump_av_sm_event_name(event));
+ return FALSE;
+
+ }
+
+ return TRUE;
+}
+/*****************************************************************************
+**
+** Function btif_av_state_opening_handler
+**
+** Description Intermediate state managing events during establishment
+** of avdtp channel
+**
+** Returns TRUE if event was processed, FALSE otherwise
+**
+*******************************************************************************/
+
+static BOOLEAN btif_av_state_opening_handler(btif_sm_event_t event, void *p_data)
+{
+ BTIF_TRACE_DEBUG("%s event:%s flags %x\n", __FUNCTION__,
+ dump_av_sm_event_name(event), btif_av_cb.flags);
+
+ switch (event) {
+ case BTIF_SM_ENTER_EVT:
+ /* inform the application that we are entering connecting state */
+ btif_report_connection_state(ESP_A2D_CONNECTION_STATE_CONNECTING, &(btif_av_cb.peer_bda));
+ break;
+
+ case BTIF_SM_EXIT_EVT:
+ break;
+
+ case BTA_AV_REJECT_EVT:
+ BTIF_TRACE_DEBUG(" Received BTA_AV_REJECT_EVT \n");
+ btif_report_connection_state(ESP_A2D_CONNECTION_STATE_DISCONNECTED, &(btif_av_cb.peer_bda));
+ btif_sm_change_state(btif_av_cb.sm_handle, BTIF_AV_STATE_IDLE);
+ break;
+
+ case BTA_AV_OPEN_EVT: {
+ tBTA_AV *p_bta_data = (tBTA_AV *)p_data;
+ esp_a2d_connection_state_t state;
+ btif_sm_state_t av_state;
+ BTIF_TRACE_DEBUG("status:%d, edr 0x%x\n", p_bta_data->open.status,
+ p_bta_data->open.edr);
+
+ if (p_bta_data->open.status == BTA_AV_SUCCESS) {
+ state = ESP_A2D_CONNECTION_STATE_CONNECTED;
+ av_state = BTIF_AV_STATE_OPENED;
+ btif_av_cb.edr = p_bta_data->open.edr;
+
+ btif_av_cb.peer_sep = p_bta_data->open.sep;
+ btif_a2dp_set_peer_sep(p_bta_data->open.sep);
+ } else {
+ BTIF_TRACE_WARNING("BTA_AV_OPEN_EVT::FAILED status: %d\n",
+ p_bta_data->open.status );
+ state = ESP_A2D_CONNECTION_STATE_DISCONNECTED;
+ av_state = BTIF_AV_STATE_IDLE;
+ }
+
+ /* inform the application of the event */
+ btif_report_connection_state(state, &(btif_av_cb.peer_bda));
+ /* change state to open/idle based on the status */
+ btif_sm_change_state(btif_av_cb.sm_handle, av_state);
+ if (btif_av_cb.peer_sep == AVDT_TSEP_SNK) {
+ /* if queued PLAY command, send it now */
+ btif_rc_check_handle_pending_play(p_bta_data->open.bd_addr,
+ (p_bta_data->open.status == BTA_AV_SUCCESS));
+ } else if (btif_av_cb.peer_sep == AVDT_TSEP_SRC) {
+ /* if queued PLAY command, send it now */
+ btif_rc_check_handle_pending_play(p_bta_data->open.bd_addr, FALSE);
+ /* Bring up AVRCP connection too */
+ BTA_AvOpenRc(btif_av_cb.bta_handle);
+ }
+ btif_queue_advance();
+ } break;
+
+ case BTIF_AV_SINK_CONFIG_REQ_EVT: {
+ if (btif_av_cb.peer_sep == AVDT_TSEP_SRC) {
+ esp_a2d_cb_param_t param;
+ memcpy(param.audio_cfg.remote_bda, &btif_av_cb.peer_bda, sizeof(esp_bd_addr_t));
+ memcpy(¶m.audio_cfg.mcc, p_data, sizeof(esp_a2d_mcc_t));
+ BTIF_A2D_CB_TO_APP(ESP_A2D_AUDIO_CFG_EVT, ¶m);
+ }
+ } break;
+
+ case BTIF_AV_CONNECT_REQ_EVT:
+ // Check for device, if same device which moved to opening then ignore callback
+ if (memcmp ((bt_bdaddr_t *)p_data, &(btif_av_cb.peer_bda),
+ sizeof(btif_av_cb.peer_bda)) == 0) {
+ BTIF_TRACE_DEBUG("%s: Same device moved to Opening state,ignore Connect Req\n", __func__);
+ btif_queue_advance();
+ break;
+ } else {
+ BTIF_TRACE_DEBUG("%s: Moved from idle by Incoming Connection request\n", __func__);
+ btif_report_connection_state(ESP_A2D_CONNECTION_STATE_DISCONNECTED, (bt_bdaddr_t *)p_data);
+ btif_queue_advance();
+ break;
+ }
+
+ case BTA_AV_PENDING_EVT:
+ // Check for device, if same device which moved to opening then ignore callback
+ if (memcmp (((tBTA_AV *)p_data)->pend.bd_addr, &(btif_av_cb.peer_bda),
+ sizeof(btif_av_cb.peer_bda)) == 0) {
+ BTIF_TRACE_DEBUG("%s: Same device moved to Opening state,ignore Pending Req\n", __func__);
+ break;
+ } else {
+ BTIF_TRACE_DEBUG("%s: Moved from idle by outgoing Connection request\n", __func__);
+ BTA_AvDisconnect(((tBTA_AV *)p_data)->pend.bd_addr);
+ break;
+ }
+
+ CHECK_RC_EVENT(event, p_data);
+
+ default:
+ BTIF_TRACE_WARNING("%s : unhandled event:%s\n", __FUNCTION__,
+ dump_av_sm_event_name(event));
+ return FALSE;
+
+ }
+ return TRUE;
+}
+
+
+/*****************************************************************************
+**
+** Function btif_av_state_closing_handler
+**
+** Description Intermediate state managing events during closing
+** of avdtp channel
+**
+** Returns TRUE if event was processed, FALSE otherwise
+**
+*******************************************************************************/
+
+static BOOLEAN btif_av_state_closing_handler(btif_sm_event_t event, void *p_data)
+{
+ BTIF_TRACE_DEBUG("%s event:%s flags %x\n", __FUNCTION__,
+ dump_av_sm_event_name(event), btif_av_cb.flags);
+
+ switch (event) {
+ case BTIF_SM_ENTER_EVT:
+ if (btif_av_cb.peer_sep == AVDT_TSEP_SRC) {
+ btif_a2dp_set_rx_flush(TRUE);
+ }
+ break;
+
+ case BTA_AV_STOP_EVT:
+ case BTIF_AV_STOP_STREAM_REQ_EVT:
+ if (btif_av_cb.peer_sep == AVDT_TSEP_SRC) {
+ btif_a2dp_set_rx_flush(TRUE);
+ }
+
+ btif_a2dp_on_stopped(NULL);
+ break;
+
+ case BTIF_SM_EXIT_EVT:
+ break;
+
+ case BTA_AV_CLOSE_EVT:
+
+ /* inform the application that we are disconnecting */
+ btif_report_connection_state(ESP_A2D_CONNECTION_STATE_DISCONNECTED, &(btif_av_cb.peer_bda));
+
+ btif_sm_change_state(btif_av_cb.sm_handle, BTIF_AV_STATE_IDLE);
+ break;
+
+ /* Handle the RC_CLOSE event for the cleanup */
+ case BTA_AV_RC_CLOSE_EVT:
+ btif_rc_handler(event, (tBTA_AV *)p_data);
+ break;
+
+ default:
+ BTIF_TRACE_WARNING("%s : unhandled event:%s\n", __FUNCTION__,
+ dump_av_sm_event_name(event));
+ return FALSE;
+ }
+ return TRUE;
+}
+
+
+/*****************************************************************************
+**
+** Function btif_av_state_opened_handler
+**
+** Description Handles AV events while AVDTP is in OPEN state
+**
+** Returns TRUE if event was processed, FALSE otherwise
+**
+*******************************************************************************/
+
+static BOOLEAN btif_av_state_opened_handler(btif_sm_event_t event, void *p_data)
+{
+ tBTA_AV *p_av = (tBTA_AV *)p_data;
+
+ BTIF_TRACE_DEBUG("%s event:%s flags %x\n", __FUNCTION__,
+ dump_av_sm_event_name(event), btif_av_cb.flags);
+
+ if ( (event == BTA_AV_REMOTE_CMD_EVT) && (btif_av_cb.flags & BTIF_AV_FLAG_REMOTE_SUSPEND) &&
+ (p_av->remote_cmd.rc_id == BTA_AV_RC_PLAY) ) {
+ BTIF_TRACE_EVENT("%s: Resetting remote suspend flag on RC PLAY\n", __FUNCTION__);
+ btif_av_cb.flags &= ~BTIF_AV_FLAG_REMOTE_SUSPEND;
+ }
+
+ switch (event) {
+ case BTIF_SM_ENTER_EVT:
+ btif_av_cb.flags &= ~BTIF_AV_FLAG_PENDING_STOP;
+ btif_av_cb.flags &= ~BTIF_AV_FLAG_PENDING_START;
+ break;
+
+ case BTIF_SM_EXIT_EVT:
+ btif_av_cb.flags &= ~BTIF_AV_FLAG_PENDING_START;
+ break;
+
+ case BTIF_AV_START_STREAM_REQ_EVT:
+ BTA_AvStart();
+ btif_av_cb.flags |= BTIF_AV_FLAG_PENDING_START;
+ break;
+
+ case BTA_AV_START_EVT: {
+ BTIF_TRACE_EVENT("BTA_AV_START_EVT status %d, suspending %d, init %d\n",
+ p_av->start.status, p_av->start.suspending, p_av->start.initiator);
+
+ if ((p_av->start.status == BTA_SUCCESS) && (p_av->start.suspending == TRUE)) {
+ return TRUE;
+ }
+
+ /* remain in open state if status failed */
+ if (p_av->start.status != BTA_AV_SUCCESS) {
+ return FALSE;
+ }
+
+ if (btif_av_cb.peer_sep == AVDT_TSEP_SRC) {
+ btif_a2dp_set_rx_flush(FALSE); /* remove flush state, ready for streaming*/
+ }
+
+ /* change state to started, send acknowledgement if start is pending */
+ if (btif_av_cb.flags & BTIF_AV_FLAG_PENDING_START) {
+
+ /* pending start flag will be cleared when exit current state */
+ }
+ btif_sm_change_state(btif_av_cb.sm_handle, BTIF_AV_STATE_STARTED);
+
+ } break;
+
+ case BTIF_AV_DISCONNECT_REQ_EVT:
+ BTA_AvClose(btif_av_cb.bta_handle);
+ if (btif_av_cb.peer_sep == AVDT_TSEP_SRC) {
+ BTA_AvCloseRc(btif_av_cb.bta_handle);
+ }
+
+ /* inform the application that we are disconnecting */
+ btif_report_connection_state(ESP_A2D_CONNECTION_STATE_DISCONNECTING, &(btif_av_cb.peer_bda));
+ break;
+
+ case BTA_AV_CLOSE_EVT:
+ /* avdtp link is closed */
+ btif_a2dp_on_stopped(NULL);
+
+ /* inform the application that we are disconnected */
+ btif_report_connection_state(ESP_A2D_CONNECTION_STATE_DISCONNECTED, &(btif_av_cb.peer_bda));
+
+ /* change state to idle, send acknowledgement if start is pending */
+ if (btif_av_cb.flags & BTIF_AV_FLAG_PENDING_START) {
+ /* pending start flag will be cleared when exit current state */
+ }
+ btif_sm_change_state(btif_av_cb.sm_handle, BTIF_AV_STATE_IDLE);
+ break;
+
+ case BTA_AV_RECONFIG_EVT:
+ if ((btif_av_cb.flags & BTIF_AV_FLAG_PENDING_START) &&
+ (p_av->reconfig.status == BTA_AV_SUCCESS)) {
+ APPL_TRACE_WARNING("reconfig done BTA_AVstart()\n");
+ BTA_AvStart();
+ } else if (btif_av_cb.flags & BTIF_AV_FLAG_PENDING_START) {
+ btif_av_cb.flags &= ~BTIF_AV_FLAG_PENDING_START;
+ }
+ break;
+
+ case BTIF_AV_CONNECT_REQ_EVT:
+ if (memcmp ((bt_bdaddr_t *)p_data, &(btif_av_cb.peer_bda),
+ sizeof(btif_av_cb.peer_bda)) == 0) {
+ BTIF_TRACE_DEBUG("%s: Ignore BTIF_AV_CONNECT_REQ_EVT for same device\n", __func__);
+ } else {
+ BTIF_TRACE_DEBUG("%s: Moved to opened by Other Incoming Conn req\n", __func__);
+ btif_report_connection_state(ESP_A2D_CONNECTION_STATE_DISCONNECTED,
+ (bt_bdaddr_t *)p_data);
+ }
+ btif_queue_advance();
+ break;
+
+ CHECK_RC_EVENT(event, p_data);
+
+ default:
+ BTIF_TRACE_WARNING("%s : unhandled event:%s\n", __FUNCTION__,
+ dump_av_sm_event_name(event));
+ return FALSE;
+
+ }
+ return TRUE;
+}
+
+/*****************************************************************************
+**
+** Function btif_av_state_started_handler
+**
+** Description Handles AV events while A2DP stream is started
+**
+** Returns TRUE if event was processed, FALSE otherwise
+**
+*******************************************************************************/
+
+static BOOLEAN btif_av_state_started_handler(btif_sm_event_t event, void *p_data)
+{
+ tBTA_AV *p_av = (tBTA_AV *)p_data;
+
+ BTIF_TRACE_DEBUG("%s event:%s flags %x\n", __FUNCTION__,
+ dump_av_sm_event_name(event), btif_av_cb.flags);
+
+ switch (event) {
+ case BTIF_SM_ENTER_EVT:
+
+ /* we are again in started state, clear any remote suspend flags */
+ btif_av_cb.flags &= ~BTIF_AV_FLAG_REMOTE_SUSPEND;
+
+ btif_report_audio_state(ESP_A2D_AUDIO_STATE_STARTED, &(btif_av_cb.peer_bda));
+
+ /* increase the a2dp consumer task priority temporarily when start
+ ** audio playing, to avoid overflow the audio packet queue. */
+ adjust_priority_a2dp(TRUE);
+
+ break;
+
+ case BTIF_SM_EXIT_EVT:
+ /* restore the a2dp consumer task priority when stop audio playing. */
+ adjust_priority_a2dp(FALSE);
+
+ break;
+
+ case BTIF_AV_START_STREAM_REQ_EVT:
+ break;
+
+ /* fixme -- use suspend = true always to work around issue with BTA AV */
+ case BTIF_AV_STOP_STREAM_REQ_EVT:
+ case BTIF_AV_SUSPEND_STREAM_REQ_EVT:
+
+ /* set pending flag to ensure btif task is not trying to restart
+ stream while suspend is in progress */
+ btif_av_cb.flags |= BTIF_AV_FLAG_LOCAL_SUSPEND_PENDING;
+
+ /* if we were remotely suspended but suspend locally, local suspend
+ always overrides */
+ btif_av_cb.flags &= ~BTIF_AV_FLAG_REMOTE_SUSPEND;
+
+ if (btif_av_cb.peer_sep == AVDT_TSEP_SRC) {
+ btif_a2dp_set_rx_flush(TRUE);
+ btif_a2dp_on_stopped(NULL);
+ }
+
+ BTA_AvStop(TRUE);
+ break;
+
+ case BTIF_AV_DISCONNECT_REQ_EVT:
+
+ /* request avdtp to close */
+ BTA_AvClose(btif_av_cb.bta_handle);
+ if (btif_av_cb.peer_sep == AVDT_TSEP_SRC) {
+ BTA_AvCloseRc(btif_av_cb.bta_handle);
+ }
+
+ /* inform the application that we are disconnecting */
+ btif_report_connection_state(ESP_A2D_CONNECTION_STATE_DISCONNECTING, &(btif_av_cb.peer_bda));
+
+ /* wait in closing state until fully closed */
+ btif_sm_change_state(btif_av_cb.sm_handle, BTIF_AV_STATE_CLOSING);
+ break;
+
+ case BTA_AV_SUSPEND_EVT:
+
+ BTIF_TRACE_EVENT("BTA_AV_SUSPEND_EVT status %d, init %d\n",
+ p_av->suspend.status, p_av->suspend.initiator);
+
+ /* a2dp suspended, stop media task until resumed */
+ btif_a2dp_on_suspended(&p_av->suspend);
+
+ /* if not successful, remain in current state */
+ if (p_av->suspend.status != BTA_AV_SUCCESS) {
+ btif_av_cb.flags &= ~BTIF_AV_FLAG_LOCAL_SUSPEND_PENDING;
+
+ return FALSE;
+ }
+
+ if (p_av->suspend.initiator != TRUE) {
+ /* remote suspend, notify HAL and await audioflinger to
+ suspend/stop stream */
+
+ /* set remote suspend flag to block media task from restarting
+ stream only if we did not already initiate a local suspend */
+ if ((btif_av_cb.flags & BTIF_AV_FLAG_LOCAL_SUSPEND_PENDING) == 0) {
+ btif_av_cb.flags |= BTIF_AV_FLAG_REMOTE_SUSPEND;
+ }
+
+ btif_report_audio_state(ESP_A2D_AUDIO_STATE_REMOTE_SUSPEND, &(btif_av_cb.peer_bda));
+ } else {
+ btif_report_audio_state(ESP_A2D_AUDIO_STATE_STOPPED, &(btif_av_cb.peer_bda));
+ }
+
+ btif_sm_change_state(btif_av_cb.sm_handle, BTIF_AV_STATE_OPENED);
+
+ /* suspend completed and state changed, clear pending status */
+ btif_av_cb.flags &= ~BTIF_AV_FLAG_LOCAL_SUSPEND_PENDING;
+ break;
+
+ case BTA_AV_STOP_EVT:
+
+ btif_av_cb.flags |= BTIF_AV_FLAG_PENDING_STOP;
+ btif_a2dp_on_stopped(&p_av->suspend);
+
+ btif_report_audio_state(ESP_A2D_AUDIO_STATE_STOPPED, &(btif_av_cb.peer_bda));
+
+ /* if stop was successful, change state to open */
+ if (p_av->suspend.status == BTA_AV_SUCCESS) {
+ btif_sm_change_state(btif_av_cb.sm_handle, BTIF_AV_STATE_OPENED);
+ }
+
+ break;
+
+ case BTA_AV_CLOSE_EVT:
+
+ btif_av_cb.flags |= BTIF_AV_FLAG_PENDING_STOP;
+
+ /* avdtp link is closed */
+ btif_a2dp_on_stopped(NULL);
+
+ /* inform the application that we are disconnected */
+ btif_report_connection_state(ESP_A2D_CONNECTION_STATE_DISCONNECTED, &(btif_av_cb.peer_bda));
+
+ btif_sm_change_state(btif_av_cb.sm_handle, BTIF_AV_STATE_IDLE);
+ break;
+
+ CHECK_RC_EVENT(event, p_data);
+
+ default:
+ BTIF_TRACE_WARNING("%s : unhandled event:%s\n", __FUNCTION__,
+ dump_av_sm_event_name(event));
+ return FALSE;
+
+ }
+ return TRUE;
+}
+
+/*****************************************************************************
+** Local event handlers
+******************************************************************************/
+
+static void btif_av_handle_event(UINT16 event, char *p_param)
+{
+ btif_sm_dispatch(btif_av_cb.sm_handle, event, (void *)p_param);
+ btif_av_event_free_data(event, p_param);
+}
+
+void btif_av_event_deep_copy(UINT16 event, char *p_dest, char *p_src)
+{
+ tBTA_AV *av_src = (tBTA_AV *)p_src;
+ tBTA_AV *av_dest = (tBTA_AV *)p_dest;
+
+ // First copy the structure
+ memcpy(p_dest, p_src, sizeof(tBTA_AV));
+
+ switch (event) {
+ case BTA_AV_META_MSG_EVT:
+ if (av_src->meta_msg.p_data && av_src->meta_msg.len) {
+ av_dest->meta_msg.p_data = osi_calloc(av_src->meta_msg.len);
+ assert(av_dest->meta_msg.p_data);
+ memcpy(av_dest->meta_msg.p_data, av_src->meta_msg.p_data, av_src->meta_msg.len);
+ }
+
+ if (av_src->meta_msg.p_msg) {
+ av_dest->meta_msg.p_msg = osi_calloc(sizeof(tAVRC_MSG));
+ assert(av_dest->meta_msg.p_msg);
+ memcpy(av_dest->meta_msg.p_msg, av_src->meta_msg.p_msg, sizeof(tAVRC_MSG));
+
+ if (av_src->meta_msg.p_msg->vendor.p_vendor_data &&
+ av_src->meta_msg.p_msg->vendor.vendor_len) {
+ av_dest->meta_msg.p_msg->vendor.p_vendor_data = osi_calloc(
+ av_src->meta_msg.p_msg->vendor.vendor_len);
+ assert(av_dest->meta_msg.p_msg->vendor.p_vendor_data);
+ memcpy(av_dest->meta_msg.p_msg->vendor.p_vendor_data,
+ av_src->meta_msg.p_msg->vendor.p_vendor_data,
+ av_src->meta_msg.p_msg->vendor.vendor_len);
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+static void btif_av_event_free_data(btif_sm_event_t event, void *p_data)
+{
+ switch (event) {
+ case BTA_AV_META_MSG_EVT: {
+ tBTA_AV *av = (tBTA_AV *)p_data;
+ if (av->meta_msg.p_data) {
+ osi_free(av->meta_msg.p_data);
+ }
+
+ if (av->meta_msg.p_msg) {
+ if (av->meta_msg.p_msg->vendor.p_vendor_data) {
+ osi_free(av->meta_msg.p_msg->vendor.p_vendor_data);
+ }
+ osi_free(av->meta_msg.p_msg);
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+static void bte_av_callback(tBTA_AV_EVT event, tBTA_AV *p_data)
+{
+ btif_transfer_context(btif_av_handle_event, event,
+ (char *)p_data, sizeof(tBTA_AV), btif_av_event_deep_copy);
+}
+
+static void bte_av_media_callback(tBTA_AV_EVT event, tBTA_AV_MEDIA *p_data)
+{
+ btif_sm_state_t state;
+ UINT8 que_len;
+ tA2D_STATUS a2d_status;
+ tA2D_SBC_CIE sbc_cie;
+ esp_a2d_mcc_t mcc;
+
+ if (event == BTA_AV_MEDIA_DATA_EVT) { /* Switch to BTIF_MEDIA context */
+ state = btif_sm_get_state(btif_av_cb.sm_handle);
+ if ( (state == BTIF_AV_STATE_STARTED) || /* send SBC packets only in Started State */
+ (state == BTIF_AV_STATE_OPENED) ) {
+ que_len = btif_media_sink_enque_buf((BT_HDR *)p_data);
+ BTIF_TRACE_DEBUG(" Packets in Que %d\n", que_len);
+ } else {
+ return;
+ }
+ }
+
+ if (event == BTA_AV_MEDIA_SINK_CFG_EVT) {
+ /* send a command to BT Media Task */
+ btif_reset_decoder((UINT8 *)p_data);
+
+ /* currently only supportes SBC */
+ a2d_status = A2D_ParsSbcInfo(&sbc_cie, (UINT8 *)p_data, FALSE);
+ if (a2d_status == A2D_SUCCESS) {
+ mcc.type = ESP_A2D_MCT_SBC;
+ memcpy(&mcc.cie, (uint8_t *)p_data + 3, ESP_A2D_CIE_LEN_SBC);
+ /* Switch to BTIF context */
+ btif_transfer_context(btif_av_handle_event, BTIF_AV_SINK_CONFIG_REQ_EVT,
+ (char *)&mcc, sizeof(mcc), NULL);
+ } else {
+ APPL_TRACE_ERROR("ERROR dump_codec_info A2D_ParsSbcInfo fail:%d\n", a2d_status);
+ }
+ }
+}
+/*******************************************************************************
+**
+** Function btif_av_init
+**
+** Description Initializes btif AV if not already done
+**
+** Returns bt_status_t
+**
+*******************************************************************************/
+
+bt_status_t btif_av_init()
+{
+ if (btif_av_cb.sm_handle == NULL) {
+ if (!btif_a2dp_start_media_task()) {
+ return BT_STATUS_FAIL;
+ }
+
+ /* Also initialize the AV state machine */
+ btif_av_cb.sm_handle =
+ btif_sm_init((const btif_sm_handler_t *)btif_av_state_handlers, BTIF_AV_STATE_IDLE);
+
+ btif_enable_service(BTA_A2DP_SOURCE_SERVICE_ID);
+#if (BTA_AV_SINK_INCLUDED == TRUE)
+ btif_enable_service(BTA_A2DP_SINK_SERVICE_ID);
+#endif
+
+ btif_a2dp_on_init();
+ }
+
+ return BT_STATUS_SUCCESS;
+}
+
+/**
+ *
+ * Function register A2DP callback
+ *
+ * Description Initializes the AV interface for sink mode
+ *
+ * Returns bt_status_t
+ *
+ */
++esp_err_t esp_a2d_register_callback(esp_a2d_cb_t callback)
+{
+ // TODO: need protection against race
+ bt_av_sink_callback = callback;
+ return ESP_OK;
+}
+
+/*******************************************************************************
+**
+** Function init_sink
+**
+** Description Initializes the AV interface for sink mode
+**
+** Returns bt_status_t
+**
+*******************************************************************************/
+esp_err_t esp_a2d_sink_init(void)
+{
+ BTIF_TRACE_EVENT("%s()\n", __func__);
+
+ bt_status_t status = btif_av_init();
+
+ return (status == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL;
+}
+
+/*******************************************************************************
+**
+** Function connect
+**
+** Description Establishes the AV signalling channel with the remote headset
+**
+** Returns bt_status_t
+**
+*******************************************************************************/
+
+static bt_status_t connect_int(bt_bdaddr_t *bd_addr, uint16_t uuid)
+{
+ btif_av_connect_req_t connect_req;
+ connect_req.target_bda = bd_addr;
+ connect_req.uuid = uuid;
+ BTIF_TRACE_EVENT("%s\n", __FUNCTION__);
+
+ btif_sm_dispatch(btif_av_cb.sm_handle, BTIF_AV_CONNECT_REQ_EVT, (char *)&connect_req);
+
+ return BT_STATUS_SUCCESS;
+}
+
+esp_err_t esp_a2d_sink_connect(esp_bd_addr_t remote_bda)
+{
+ BTIF_TRACE_EVENT("%s\n", __FUNCTION__);
+ CHECK_BTAV_INIT();
+
+ bt_status_t stat;
+ bt_bdaddr_t bd_addr;
+ memcpy(&bd_addr, remote_bda, sizeof(bt_bdaddr_t));
+
+ stat = btif_queue_connect(UUID_SERVCLASS_AUDIO_SINK, &bd_addr, connect_int);
+ return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL;
+}
+
+esp_err_t esp_a2d_sink_disconnect(esp_bd_addr_t remote_bda)
+{
+ bt_status_t stat;
+ bt_bdaddr_t bd_addr;
+ BTIF_TRACE_EVENT("%s\n", __FUNCTION__);
+ CHECK_BTAV_INIT();
+ memcpy(&bd_addr, remote_bda, sizeof(bt_bdaddr_t));
+ /* Switch to BTIF context */
+ stat = btif_transfer_context(btif_av_handle_event, BTIF_AV_DISCONNECT_REQ_EVT,
+ (char *)(&bd_addr), sizeof(bt_bdaddr_t), NULL);
+ return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL;
+}
+/*******************************************************************************
+**
+** Function cleanup
+**
+** Description Shuts down the AV interface and does the cleanup
+**
+** Returns None
+**
+*******************************************************************************/
+static void cleanup(void)
+{
+ BTIF_TRACE_EVENT("%s\n", __FUNCTION__);
+
+ btif_a2dp_stop_media_task();
+
+ btif_disable_service(BTA_A2DP_SOURCE_SERVICE_ID);
+#if (BTA_AV_SINK_INCLUDED == TRUE)
+ btif_disable_service(BTA_A2DP_SINK_SERVICE_ID);
+#endif
+
+ /* Also shut down the AV state machine */
+ btif_sm_shutdown(btif_av_cb.sm_handle);
+ btif_av_cb.sm_handle = NULL;
+}
+
+void esp_a2d_sink_deinit(void)
+{
+ BTIF_TRACE_EVENT("%s\n", __FUNCTION__);
+
+ if (bt_av_sink_callback) {
+ bt_av_sink_callback = NULL;
+ cleanup();
+ }
+}
+
+/*******************************************************************************
+**
+** Function btif_av_get_sm_handle
+**
+** Description Fetches current av SM handle
+**
+** Returns None
+**
+*******************************************************************************/
+
+btif_sm_handle_t btif_av_get_sm_handle(void)
+{
+ return btif_av_cb.sm_handle;
+}
+
+/*******************************************************************************
+**
+** Function btif_av_stream_ready
+**
+** Description Checks whether AV is ready for starting a stream
+**
+** Returns None
+**
+*******************************************************************************/
+
+BOOLEAN btif_av_stream_ready(void)
+{
+ btif_sm_state_t state = btif_sm_get_state(btif_av_cb.sm_handle);
+
+ BTIF_TRACE_DEBUG("btif_av_stream_ready : sm hdl %d, state %d, flags %x\n",
+ (int)btif_av_cb.sm_handle, state, btif_av_cb.flags);
+
+ /* also make sure main adapter is enabled */
+ if (btif_is_enabled() == 0) {
+ BTIF_TRACE_EVENT("main adapter not enabled");
+ return FALSE;
+ }
+
+ /* check if we are remotely suspended or stop is pending */
+ if (btif_av_cb.flags & (BTIF_AV_FLAG_REMOTE_SUSPEND | BTIF_AV_FLAG_PENDING_STOP)) {
+ return FALSE;
+ }
+
+ return (state == BTIF_AV_STATE_OPENED);
+}
+
+/*******************************************************************************
+**
+** Function btif_av_stream_started_ready
+**
+** Description Checks whether AV ready for media start in streaming state
+**
+** Returns None
+**
+*******************************************************************************/
+
+BOOLEAN btif_av_stream_started_ready(void)
+{
+ btif_sm_state_t state = btif_sm_get_state(btif_av_cb.sm_handle);
+
+ BTIF_TRACE_DEBUG("btif_av_stream_started : sm hdl %d, state %d, flags %x\n",
+ (int)btif_av_cb.sm_handle, state, btif_av_cb.flags);
+
+ /* disallow media task to start if we have pending actions */
+ if (btif_av_cb.flags & (BTIF_AV_FLAG_LOCAL_SUSPEND_PENDING | BTIF_AV_FLAG_REMOTE_SUSPEND
+ | BTIF_AV_FLAG_PENDING_STOP)) {
+ return FALSE;
+ }
+
+ return (state == BTIF_AV_STATE_STARTED);
+}
+
+/*******************************************************************************
+**
+** Function btif_dispatch_sm_event
+**
+** Description Send event to AV statemachine
+**
+** Returns None
+**
+*******************************************************************************/
+
+/* used to pass events to AV statemachine from other tasks */
+void btif_dispatch_sm_event(btif_av_sm_event_t event, void *p_data, int len)
+{
+ /* Switch to BTIF context */
+ btif_transfer_context(btif_av_handle_event, event,
+ (char *)p_data, len, NULL);
+}
+
+/*******************************************************************************
+**
+** Function btif_av_execute_service
+**
+** Description Initializes/Shuts down the service
+**
+** Returns BT_STATUS_SUCCESS on success, BT_STATUS_FAIL otherwise
+**
+*******************************************************************************/
+bt_status_t btif_av_execute_service(BOOLEAN b_enable)
+{
+ if (b_enable) {
+ /* TODO: Removed BTA_SEC_AUTHORIZE since the Java/App does not
+ * handle this request in order to allow incoming connections to succeed.
+ * We need to put this back once support for this is added */
+
+ /* Added BTA_AV_FEAT_NO_SCO_SSPD - this ensures that the BTA does not
+ * auto-suspend av streaming on AG events(SCO or Call). The suspend shall
+ * be initiated by the app/audioflinger layers */
+ BTA_AvEnable(BTA_SEC_AUTHENTICATE, (BTA_AV_FEAT_NO_SCO_SSPD)
+ // | BTA_AV_FEAT_RCTG | BTA_AV_FEAT_METADATA | BTA_AV_FEAT_VENDOR
+ | BTA_AV_FEAT_RCCT | BTA_AV_FEAT_ADV_CTRL,
+ bte_av_callback);
+ BTA_AvRegister(BTA_AV_CHNL_AUDIO, BTIF_AV_SERVICE_NAME, 0, bte_av_media_callback, &bta_av_a2d_cos);
+ } else {
+ BTA_AvDeregister(btif_av_cb.bta_handle);
+ BTA_AvDisable();
+ }
+ return BT_STATUS_SUCCESS;
+}
+
+/*******************************************************************************
+**
+** Function btif_av_sink_execute_service
+**
+** Description Initializes/Shuts down the service
+**
+** Returns BT_STATUS_SUCCESS on success, BT_STATUS_FAIL otherwise
+**
+*******************************************************************************/
+bt_status_t btif_av_sink_execute_service(BOOLEAN b_enable)
+{
+#if (BTA_AV_SINK_INCLUDED == TRUE)
+ BTA_AvEnable_Sink(b_enable);
+#endif
+ return BT_STATUS_SUCCESS;
+}
+
+/*******************************************************************************
+**
+** Function btif_av_is_connected
+**
+** Description Checks if av has a connected sink
+**
+** Returns BOOLEAN
+**
+*******************************************************************************/
+BOOLEAN btif_av_is_connected(void)
+{
+ btif_sm_state_t state = btif_sm_get_state(btif_av_cb.sm_handle);
+ return ((state == BTIF_AV_STATE_OPENED) || (state == BTIF_AV_STATE_STARTED));
+}
+
+/*******************************************************************************
+**
+** Function btif_av_is_peer_edr
+**
+** Description Check if the connected a2dp device supports
+** EDR or not. Only when connected this function
+** will accurately provide a true capability of
+** remote peer. If not connected it will always be false.
+**
+** Returns TRUE if remote device is capable of EDR
+**
+*******************************************************************************/
+BOOLEAN btif_av_is_peer_edr(void)
+{
+ ASSERTC(btif_av_is_connected(), "No active a2dp connection\n", 0);
+
+ if (btif_av_cb.edr) {
+ return TRUE;
+ } else {
+ return FALSE;
+ }
+}
+
+/******************************************************************************
+**
+** Function btif_av_clear_remote_suspend_flag
+**
+** Description Clears btif_av_cd.flags if BTIF_AV_FLAG_REMOTE_SUSPEND is set
+**
+** Returns void
+******************************************************************************/
+void btif_av_clear_remote_suspend_flag(void)
+{
+ BTIF_TRACE_DEBUG("%s: flag :%x\n", __func__, btif_av_cb.flags);
+ btif_av_cb.flags &= ~BTIF_AV_FLAG_REMOTE_SUSPEND;
+}
--- /dev/null
- xTaskCreate(btif_task_thread_handler, "BtifT", 2048, NULL, configMAX_PRIORITIES - 1, &xBtifTaskHandle);
+/******************************************************************************
+ *
+ * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (C) 2009-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.
+ *
+ ******************************************************************************/
+
+/************************************************************************************
+ *
+ * Filename: btif_core.c
+ *
+ * Description: Contains core functionality related to interfacing between
+ * Bluetooth HAL and BTE core stack.
+ *
+ ***********************************************************************************/
+
+#include <ctype.h>
+// #include <cutils/properties.h>
+// #include <dirent.h>
+// #include <fcntl.h>
+// #include <hardware/bluetooth.h>
+#include <stdlib.h>
+#include <string.h>
+// #include <sys/stat.h>
+// #include <sys/types.h>
+
+#define LOG_TAG "bt_btif_core"
+// #include "btcore/include/bdaddr.h"
+
+#include "bdaddr.h"
+// #include "bt_utils.h"
+#include "bta_api.h"
+#include "bte.h"
+#include "btif_api.h"
+// #include "btif_av.h"
+// #include "btif_config.h"
+// #include "btif_pan.h"
+// #include "btif_profile_queue.h"
+// #include "btif_config.h"
+// #include "btif_sock.h"
+// #include "btif_storage.h"
+#include "btif_util.h"
+#include "btu.h"
+#include "controller.h"
+#include "fixed_queue.h"
+#include "future.h"
+#include "gki.h"
+#include "osi.h"
+// #include "osi/include/log.h"
+#include "stack_manager.h"
+#include "thread.h"
+#include "btif_common.h"
+#include "btif_dm.h"
+/************************************************************************************
+** Constants & Macros
+************************************************************************************/
+
+/************************************************************************************
+** Local type definitions
+************************************************************************************/
+
+/************************************************************************************
+** Static variables
+************************************************************************************/
+
+static tBTA_SERVICE_MASK btif_enabled_services = 0;
+
+static fixed_queue_t *btif_msg_queue = NULL;
+static xTaskHandle xBtifTaskHandle = NULL;
+
+/************************************************************************************
+** Static functions
+************************************************************************************/
+
+/* sends message to btif task */
+static void btif_sendmsg(void *p_msg);
+static void btif_thread_post(uint32_t sig);
+/************************************************************************************
+** Externs
+************************************************************************************/
+static fixed_queue_t *xBtifQueue = NULL;
+
+/** TODO: Move these to _common.h */
+void bte_main_boot_entry(void *);
+void bte_main_disable(void);
+void bte_main_shutdown(void);
+void btif_dm_execute_service_request(UINT16 event, char *p_param);
+
+/*******************************************************************************
+**
+** Function btif_context_switched
+**
+** Description Callback used to execute transferred context callback
+**
+** p_msg : message to be executed in btif context
+**
+** Returns void
+**
+*******************************************************************************/
+
+static void btif_context_switched(void *p_msg)
+{
+
+ BTIF_TRACE_VERBOSE("btif_context_switched");
+
+ tBTIF_CONTEXT_SWITCH_CBACK *p = (tBTIF_CONTEXT_SWITCH_CBACK *) p_msg;
+
+ /* each callback knows how to parse the data */
+ if (p->p_cb) {
+ p->p_cb(p->event, p->p_param);
+ }
+}
+
+
+/*******************************************************************************
+**
+** Function btif_transfer_context
+**
+** Description This function switches context to btif task
+**
+** p_cback : callback used to process message in btif context
+** event : event id of message
+** p_params : parameter area passed to callback (copied)
+** param_len : length of parameter area
+** p_copy_cback : If set this function will be invoked for deep copy
+**
+** Returns void
+**
+*******************************************************************************/
+
+bt_status_t btif_transfer_context (tBTIF_CBACK *p_cback, UINT16 event, char *p_params, int param_len, tBTIF_COPY_CBACK *p_copy_cback)
+{
+ tBTIF_CONTEXT_SWITCH_CBACK *p_msg;
+
+ BTIF_TRACE_VERBOSE("btif_transfer_context event %d, len %d", event, param_len);
+
+ /* allocate and send message that will be executed in btif context */
+ if ((p_msg = (tBTIF_CONTEXT_SWITCH_CBACK *) GKI_getbuf(sizeof(tBTIF_CONTEXT_SWITCH_CBACK) + param_len)) != NULL) {
+ p_msg->hdr.event = BT_EVT_CONTEXT_SWITCH_EVT; /* internal event */
+ p_msg->p_cb = p_cback;
+
+ p_msg->event = event; /* callback event */
+
+ /* check if caller has provided a copy callback to do the deep copy */
+ if (p_copy_cback) {
+ p_copy_cback(event, p_msg->p_param, p_params);
+ } else if (p_params) {
+ memcpy(p_msg->p_param, p_params, param_len); /* callback parameter data */
+ }
+
+ btif_sendmsg(p_msg);
+ return BT_STATUS_SUCCESS;
+ } else {
+ /* let caller deal with a failed allocation */
+ return BT_STATUS_NOMEM;
+ }
+}
+
+int btif_is_enabled(void)
+{
+ return (stack_manager_is_stack_running());
+}
+
+void btif_init_ok(void)
+{
+ BTIF_TRACE_DEBUG("btif_task: received trigger stack init event");
+ future_ready(stack_manager_get_hack_future(), FUTURE_SUCCESS);
+}
+
+/*******************************************************************************
+**
+** Function btif_enable_bluetooth_evt
+**
+** Description Event indicating bluetooth enable is completed
+** Notifies HAL user with updated adapter state
+**
+** Returns void
+**
+*******************************************************************************/
+
+void btif_enable_bluetooth_evt(tBTA_STATUS status)
+{
+ if (status == BTA_SUCCESS) {
+ future_ready(stack_manager_get_hack_future(), FUTURE_SUCCESS);
+ } else {
+ future_ready(stack_manager_get_hack_future(), FUTURE_FAIL);
+ }
+}
+
+/*******************************************************************************
+**
+** Function btif_disable_bluetooth_evt
+**
+** Description Event notifying BT disable is now complete.
+** Terminates main stack tasks and notifies HAL
+** user with updated BT state.
+**
+** Returns void
+**
+*******************************************************************************/
+
+void btif_disable_bluetooth_evt(void)
+{
+ BTIF_TRACE_DEBUG("%s", __FUNCTION__);
+
+ /* callback to HAL */
+ future_ready(stack_manager_get_hack_future(), FUTURE_SUCCESS);
+}
+
+/*******************************************************************************
+**
+** Function btif_task
+**
+** Description BTIF task handler managing all messages being passed
+** Bluetooth HAL and BTA.
+**
+** Returns void
+**
+*******************************************************************************/
+static void bt_jni_msg_ready(fixed_queue_t *queue)
+{
+ BT_HDR *p_msg;
+ while (!fixed_queue_is_empty(queue)) {
+ p_msg = (BT_HDR *)fixed_queue_dequeue(queue);
+ BTIF_TRACE_VERBOSE("btif task fetched event %x", p_msg->event);
+ switch (p_msg->event) {
+ case BT_EVT_CONTEXT_SWITCH_EVT:
+ btif_context_switched(p_msg);
+ break;
+ default:
+ BTIF_TRACE_ERROR("unhandled btif event (%d)", p_msg->event & BT_EVT_MASK); break;
+ }
+ GKI_freebuf(p_msg);
+ }
+}
+
+/*******************************************************************************
+**
+** Function btif_sendmsg
+**
+** Description Sends msg to BTIF task
+**
+** Returns void
+**
+*******************************************************************************/
+
+void btif_sendmsg(void *p_msg)
+{
+ fixed_queue_enqueue(btif_msg_queue, p_msg);
+ btif_thread_post(SIG_BTIF_WORK);
+}
+
+static void btif_thread_post(uint32_t sig)
+{
+ BtTaskEvt_t *evt = (BtTaskEvt_t *)osi_malloc(sizeof(BtTaskEvt_t));
+ if (evt == NULL) {
+ return;
+ }
+
+ evt->sig = sig;
+ evt->par = 0;
+
+ if (xQueueSend(xBtifQueue, &evt, 10 / portTICK_RATE_MS) != pdTRUE) {
+ ets_printf("xBtifQueue failed\n");
+ }
+}
+
+/*****************************************************************************
+**
+** Function btif_task_thread_handler
+**
+** Description Process BTif Task Thread.
+******************************************************************************/
+void btif_task_thread_handler(void *arg)
+{
+ BtTaskEvt_t *e;
+ for (;;) {
+ if (pdTRUE == xQueueReceive(xBtifQueue, &e, (portTickType)portMAX_DELAY)) {
+ if (e->sig == SIG_BTIF_WORK) {
+ fixed_queue_process(btif_msg_queue);
+ }
+ osi_free(e);
+ }
+ }
+}
+
+/*******************************************************************************
+**
+** Function btif_init_bluetooth
+**
+** Description Creates BTIF task and prepares BT scheduler for startup
+**
+** Returns bt_status_t
+**
+*******************************************************************************/
+bt_status_t btif_init_bluetooth(void)
+{
+ bte_main_boot_entry(btif_init_ok);
+
+ btif_msg_queue = fixed_queue_new(SIZE_MAX);
+ if (btif_msg_queue == NULL) {
+ goto error_exit;
+ }
+ xBtifQueue = xQueueCreate(60, sizeof(void *));
++ xTaskCreatePinnedToCore(btif_task_thread_handler, "BtifT", 2048, NULL, configMAX_PRIORITIES - 1, &xBtifTaskHandle, 0);
+ fixed_queue_register_dequeue(btif_msg_queue, bt_jni_msg_ready);
+
+ return BT_STATUS_SUCCESS;
+
+error_exit:;
+ btif_shutdown_bluetooth();
+
+ return BT_STATUS_FAIL;
+}
+
+/*******************************************************************************
+**
+** Function btif_enable_bluetooth
+**
+** Description Inititates shutdown of Bluetooth system.
+** Any active links will be dropped and device entering
+** non connectable/discoverable mode
+**
+** Returns void
+**
+*******************************************************************************/
+bt_status_t btif_enable_bluetooth(void)
+{
+ BTIF_TRACE_DEBUG("BTIF ENABLE BLUETOOTH");
+
+ BTA_EnableBluetooth(bte_dm_evt);
+
+ return BT_STATUS_SUCCESS;
+}
+
+/*******************************************************************************
+**
+** Function btif_disable_bluetooth
+**
+** Description Inititates shutdown of Bluetooth system.
+** Any active links will be dropped and device entering
+** non connectable/discoverable mode
+**
+** Returns void
+**
+*******************************************************************************/
+bt_status_t btif_disable_bluetooth(void)
+{
+ BTIF_TRACE_DEBUG("BTIF DISABLE BLUETOOTH");
+
+ // btif_dm_on_disable();
+ /* cleanup rfcomm & l2cap api */
+ // btif_sock_cleanup();
+ // btif_pan_cleanup();
+ BTA_DisableBluetooth();
+
+ return BT_STATUS_SUCCESS;
+}
+
+/*******************************************************************************
+**
+** Function btif_shutdown_bluetooth
+**
+** Description Finalizes BT scheduler shutdown and terminates BTIF
+** task.
+**
+** Returns void
+**
+*******************************************************************************/
+
+bt_status_t btif_shutdown_bluetooth(void)
+{
+ BTIF_TRACE_DEBUG("%s", __FUNCTION__);
+
+ fixed_queue_unregister_dequeue(btif_msg_queue);
+ fixed_queue_free(btif_msg_queue, NULL);
+ btif_msg_queue = NULL;
+
+ vTaskDelete(xBtifTaskHandle);
+ xBtifTaskHandle = NULL;
+
+ vQueueDelete(xBtifQueue);
+ xBtifQueue = NULL;
+
+ bte_main_shutdown();
+
+ return BT_STATUS_SUCCESS;
+}
+
+/*******************************************************************************
+**
+** Function btif_get_enabled_services_mask
+**
+** Description Fetches currently enabled services
+**
+** Returns tBTA_SERVICE_MASK
+**
+*******************************************************************************/
+
+tBTA_SERVICE_MASK btif_get_enabled_services_mask(void)
+{
+ return btif_enabled_services;
+}
+
+/*******************************************************************************
+**
+** Function btif_enable_service
+**
+** Description Enables the service 'service_ID' to the service_mask.
+** Upon BT enable, BTIF core shall invoke the BTA APIs to
+** enable the profiles
+**
+** Returns bt_status_t
+**
+*******************************************************************************/
+bt_status_t btif_enable_service(tBTA_SERVICE_ID service_id)
+{
+ tBTA_SERVICE_ID *p_id = &service_id;
+
+ /* If BT is enabled, we need to switch to BTIF context and trigger the
+ * enable for that profile
+ *
+ * Otherwise, we just set the flag. On BT_Enable, the DM will trigger
+ * enable for the profiles that have been enabled */
+
+ btif_enabled_services |= (1 << service_id);
+
+ BTIF_TRACE_DEBUG("%s: current services:0x%x", __FUNCTION__, btif_enabled_services);
+
+ if (btif_is_enabled()) {
+ btif_transfer_context(btif_dm_execute_service_request,
+ BTIF_DM_ENABLE_SERVICE,
+ (char *)p_id, sizeof(tBTA_SERVICE_ID), NULL);
+ }
+
+ return BT_STATUS_SUCCESS;
+}
+/*******************************************************************************
+**
+** Function btif_disable_service
+**
+** Description Disables the service 'service_ID' to the service_mask.
+** Upon BT disable, BTIF core shall invoke the BTA APIs to
+** disable the profiles
+**
+** Returns bt_status_t
+**
+*******************************************************************************/
+bt_status_t btif_disable_service(tBTA_SERVICE_ID service_id)
+{
+ tBTA_SERVICE_ID *p_id = &service_id;
+
+ /* If BT is enabled, we need to switch to BTIF context and trigger the
+ * disable for that profile so that the appropriate uuid_property_changed will
+ * be triggerred. Otherwise, we just need to clear the service_id in the mask
+ */
+
+ btif_enabled_services &= (tBTA_SERVICE_MASK)(~(1 << service_id));
+
+ BTIF_TRACE_DEBUG("%s: Current Services:0x%x", __FUNCTION__, btif_enabled_services);
+
+ if (btif_is_enabled()) {
+ btif_transfer_context(btif_dm_execute_service_request,
+ BTIF_DM_DISABLE_SERVICE,
+ (char *)p_id, sizeof(tBTA_SERVICE_ID), NULL);
+ }
+
+ return BT_STATUS_SUCCESS;
+}
--- /dev/null
- xTaskCreate(btif_media_task_handler, "BtifMediaT\n", 2048, NULL, configMAX_PRIORITIES - 1, &xBtifMediaTaskHandle);
+/******************************************************************************
+ *
+ * Copyright (C) 2009-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.
+ *
+ ******************************************************************************/
+
+/******************************************************************************
+ **
+ ** Name: btif_media_task.c
+ **
+ ** Description: This is the multimedia module for the BTIF system. It
+ ** contains task implementations AV, HS and HF profiles
+ ** audio & video processing
+ **
+ ******************************************************************************/
+
+#define LOG_TAG "bt_btif_media"
+
+#include "bt_trace.h"
+#include <string.h>
+#include <stdio.h>
+#include <stdint.h>
+
+#include "bt_target.h"
+#include "fixed_queue.h"
+#include "gki.h"
+#include "bta_api.h"
+#include "btu.h"
+#include "bta_sys.h"
+#include "bta_sys_int.h"
+
+#include "bta_av_api.h"
+#include "a2d_api.h"
+#include "a2d_sbc.h"
+#include "a2d_int.h"
+#include "bta_av_sbc.h"
+#include "bta_av_ci.h"
+#include "l2c_api.h"
+
+#include "btif_av_co.h"
+#include "btif_media.h"
+
+#include "alarm.h"
+#include "bt_trace.h"
+#include "thread.h"
+
+#include "bt_defs.h"
+#include "btif_av.h"
+#include "btif_sm.h"
+#include "btif_util.h"
+#if (BTA_AV_SINK_INCLUDED == TRUE)
+#include "oi_codec_sbc.h"
+#include "oi_status.h"
+#endif
+#include "stdio.h"
+
+#include "btif_media.h"
+#include "allocator.h"
+#include "bt_utils.h"
+#include "esp_a2dp_api.h"
+
+#if (BTA_AV_SINK_INCLUDED == TRUE)
+OI_CODEC_SBC_DECODER_CONTEXT context;
+OI_UINT32 contextData[CODEC_DATA_WORDS(2, SBC_CODEC_FAST_FILTER_BUFFERS)];
+OI_INT16 pcmData[15 * SBC_MAX_SAMPLES_PER_FRAME * SBC_MAX_CHANNELS];
+#endif
+
+
+/*****************************************************************************
+ ** Constants
+ *****************************************************************************/
+
+#ifndef AUDIO_CHANNEL_OUT_MONO
+#define AUDIO_CHANNEL_OUT_MONO 0x01
+#endif
+
+#ifndef AUDIO_CHANNEL_OUT_STEREO
+#define AUDIO_CHANNEL_OUT_STEREO 0x03
+#endif
+
+/* BTIF media cmd event definition : BTIF_MEDIA_TASK_CMD */
+enum {
+ BTIF_MEDIA_START_AA_TX = 1,
+ BTIF_MEDIA_STOP_AA_TX,
+ BTIF_MEDIA_AA_RX_RDY,
+ BTIF_MEDIA_UIPC_RX_RDY,
+ BTIF_MEDIA_SBC_ENC_INIT,
+ BTIF_MEDIA_SBC_ENC_UPDATE,
+ BTIF_MEDIA_SBC_DEC_INIT,
+ BTIF_MEDIA_VIDEO_DEC_INIT,
+ BTIF_MEDIA_FLUSH_AA_TX,
+ BTIF_MEDIA_FLUSH_AA_RX,
+ BTIF_MEDIA_AUDIO_FEEDING_INIT,
+ BTIF_MEDIA_AUDIO_RECEIVING_INIT,
+ BTIF_MEDIA_AUDIO_SINK_CFG_UPDATE,
+ BTIF_MEDIA_AUDIO_SINK_CLEAR_TRACK
+};
+
+enum {
+ MEDIA_TASK_STATE_OFF = 0,
+ MEDIA_TASK_STATE_ON = 1,
+ MEDIA_TASK_STATE_SHUTTING_DOWN = 2
+};
+
+enum {
+ SIG_MEDIA_TASK_INIT = 0xf0,
+ SIG_MEDIA_TASK_CLEAN_UP = 0xf1,
+ SIG_MEDIA_TASK_AVK_ALARM_TO = 0xf2,
+ SIG_MEDIA_TASK_AA_ALARM_TO = 0xf3,
+ SIG_MEDIA_TASK_CMD_READY = 0xf4
+};
+
+/* Macro to multiply the media task tick */
+#ifndef BTIF_MEDIA_NUM_TICK
+#define BTIF_MEDIA_NUM_TICK 1
+#endif
+
+/* Media task tick in milliseconds, must be set to multiple of
+ (1000/TICKS_PER_SEC) (10) */
+
+#define BTIF_MEDIA_TIME_TICK (20 * BTIF_MEDIA_NUM_TICK)
+#define A2DP_DATA_READ_POLL_MS (BTIF_MEDIA_TIME_TICK / 2)
+#define BTIF_SINK_MEDIA_TIME_TICK (20 * BTIF_MEDIA_NUM_TICK)
+
+
+/* buffer pool */
+#define BTIF_MEDIA_AA_POOL_ID GKI_POOL_ID_3
+#define BTIF_MEDIA_AA_BUF_SIZE GKI_BUF3_SIZE
+
+/* offset */
+#if (BTA_AV_CO_CP_SCMS_T == TRUE)
+#define BTIF_MEDIA_AA_SBC_OFFSET (AVDT_MEDIA_OFFSET + BTA_AV_SBC_HDR_SIZE + 1)
+#else
+#define BTIF_MEDIA_AA_SBC_OFFSET (AVDT_MEDIA_OFFSET + BTA_AV_SBC_HDR_SIZE)
+#endif
+
+/* Define the bitrate step when trying to match bitpool value */
+#ifndef BTIF_MEDIA_BITRATE_STEP
+#define BTIF_MEDIA_BITRATE_STEP 5
+#endif
+
+/* Middle quality quality setting @ 44.1 khz */
+#define DEFAULT_SBC_BITRATE 328
+
+#ifndef BTIF_A2DP_NON_EDR_MAX_RATE
+#define BTIF_A2DP_NON_EDR_MAX_RATE 229
+#endif
+
+#define USEC_PER_SEC 1000000L
+#define TPUT_STATS_INTERVAL_US (3000*1000)
+
+/*
+ * CONGESTION COMPENSATION CTRL ::
+ *
+ * Thus setting controls how many buffers we will hold in media task
+ * during temp link congestion. Together with the stack buffer queues
+ * it controls much temporary a2dp link congestion we can
+ * compensate for. It however also depends on the default run level of sinks
+ * jitterbuffers. Depending on type of sink this would vary.
+ * Ideally the (SRC) max tx buffer capacity should equal the sinks
+ * jitterbuffer runlevel including any intermediate buffers on the way
+ * towards the sinks codec.
+ */
+
+/* fixme -- define this in pcm time instead of buffer count */
+
+/* The typical runlevel of the tx queue size is ~1 buffer
+ but due to link flow control or thread preemption in lower
+ layers we might need to temporarily buffer up data */
+
+/* 18 frames is equivalent to 6.89*18*2.9 ~= 360 ms @ 44.1 khz, 20 ms mediatick */
+#define MAX_OUTPUT_A2DP_FRAME_QUEUE_SZ 18
+
+#ifndef MAX_PCM_FRAME_NUM_PER_TICK
+#define MAX_PCM_FRAME_NUM_PER_TICK 14
+#endif
+
+/* In case of A2DP SINK, we will delay start by 5 AVDTP Packets*/
+#define MAX_A2DP_DELAYED_START_FRAME_COUNT 5
+#define PACKET_PLAYED_PER_TICK_48 8
+#define PACKET_PLAYED_PER_TICK_44 7
+#define PACKET_PLAYED_PER_TICK_32 5
+#define PACKET_PLAYED_PER_TICK_16 3
+
+typedef struct {
+ UINT16 num_frames_to_be_processed;
+ UINT16 len;
+ UINT16 offset;
+ UINT16 layer_specific;
+} tBT_SBC_HDR;
+
+typedef struct {
+ UINT32 aa_frame_counter;
+ INT32 aa_feed_counter;
+ INT32 aa_feed_residue;
+ UINT32 counter;
+ UINT32 bytes_per_tick; /* pcm bytes read each media task tick */
+} tBTIF_AV_MEDIA_FEEDINGS_PCM_STATE;
+
+typedef union {
+ tBTIF_AV_MEDIA_FEEDINGS_PCM_STATE pcm;
+} tBTIF_AV_MEDIA_FEEDINGS_STATE;
+
+typedef struct {
+#if (BTA_AV_INCLUDED == TRUE)
+ BUFFER_Q RxSbcQ;
+ BOOLEAN is_tx_timer;
+ BOOLEAN is_rx_timer;
+ UINT8 busy_level;
+ void *av_sm_hdl;
+ UINT8 a2dp_cmd_pending; /* we can have max one command pending */
+ BOOLEAN rx_flush; /* discards any incoming data when true */
+ UINT8 peer_sep;
+ BOOLEAN data_channel_open;
+ UINT8 frames_to_process;
+
+ UINT32 sample_rate;
+ UINT8 channel_count;
+ osi_alarm_t *decode_alarm;
+#endif
+
+} tBTIF_MEDIA_CB;
+
+typedef struct {
+ long long rx;
+ long long rx_tot;
+ long long tx;
+ long long tx_tot;
+ long long ts_prev_us;
+} t_stat;
+
+#if (BTA_AV_SINK_INCLUDED == TRUE)
+extern OI_STATUS OI_CODEC_SBC_DecodeFrame(OI_CODEC_SBC_DECODER_CONTEXT *context,
+ const OI_BYTE **frameData,
+ unsigned long *frameBytes,
+ OI_INT16 *pcmData,
+ unsigned long *pcmBytes);
+extern OI_STATUS OI_CODEC_SBC_DecoderReset(OI_CODEC_SBC_DECODER_CONTEXT *context,
+ unsigned long *decoderData,
+ unsigned long decoderDataBytes,
+ OI_UINT8 maxChannels,
+ OI_UINT8 pcmStride,
+ OI_BOOL enhanced);
+#endif
+static void btif_media_flush_q(BUFFER_Q *p_q);
+static void btif_media_task_aa_handle_stop_decoding(void );
+static void btif_media_task_aa_rx_flush(void);
+
+static const char *dump_media_event(UINT16 event);
+static void btif_media_thread_handle_cmd(fixed_queue_t *queue);
+
+/* Handle incoming media packets A2DP SINK streaming*/
+#if (BTA_AV_SINK_INCLUDED == TRUE)
+static void btif_media_task_handle_inc_media(tBT_SBC_HDR *p_msg);
+#endif
+
+#if (BTA_AV_INCLUDED == TRUE)
+#if (BTA_AV_SINK_INCLUDED == TRUE)
+static void btif_media_task_aa_handle_decoder_reset(BT_HDR *p_msg);
+static void btif_media_task_aa_handle_clear_track(void);
+#endif
+static void btif_media_task_aa_handle_start_decoding(void);
+#endif
+BOOLEAN btif_media_task_clear_track(void);
+static void btif_media_task_handler(void *arg);
+
+static void btif_media_task_avk_handle_timer(UNUSED_ATTR void *context);
+static void btif_media_thread_init(UNUSED_ATTR void *context);
+static void btif_media_thread_cleanup(UNUSED_ATTR void *context);
+extern BOOLEAN btif_hf_is_call_idle();
+
+static tBTIF_MEDIA_CB btif_media_cb;
+static int media_task_running = MEDIA_TASK_STATE_OFF;
+
+static fixed_queue_t *btif_media_cmd_msg_queue = NULL;
+static xTaskHandle xBtifMediaTaskHandle = NULL;
+static QueueHandle_t xBtifMediaQueue = NULL;
+
+static esp_a2d_data_cb_t bt_av_sink_data_callback = NULL;
+
+esp_err_t esp_a2d_register_data_callback(esp_a2d_data_cb_t cb)
+{
+ // TODO: need protection against race
+ bt_av_sink_data_callback = cb;
+ return ESP_OK;
+}
+
+// TODO: need protection against race
+#define BTIF_A2D_DATA_CB_TO_APP(data, len) do { \
+ if (bt_av_sink_data_callback) { \
+ bt_av_sink_data_callback(data, len); \
+ } \
+ } while (0)
+
+/*****************************************************************************
+ ** temporary hacked functions. TODO: port these functions or remove them?
+ *****************************************************************************/
+BOOLEAN btif_hf_is_call_idle(void)
+{
+ return FALSE;
+}
+
+/*****************************************************************************
+ ** Misc helper functions
+ *****************************************************************************/
+
+UNUSED_ATTR static const char *dump_media_event(UINT16 event)
+{
+ switch (event) {
+ CASE_RETURN_STR(BTIF_MEDIA_START_AA_TX)
+ CASE_RETURN_STR(BTIF_MEDIA_STOP_AA_TX)
+ CASE_RETURN_STR(BTIF_MEDIA_AA_RX_RDY)
+ CASE_RETURN_STR(BTIF_MEDIA_UIPC_RX_RDY)
+ CASE_RETURN_STR(BTIF_MEDIA_SBC_ENC_INIT)
+ CASE_RETURN_STR(BTIF_MEDIA_SBC_ENC_UPDATE)
+ CASE_RETURN_STR(BTIF_MEDIA_SBC_DEC_INIT)
+ CASE_RETURN_STR(BTIF_MEDIA_VIDEO_DEC_INIT)
+ CASE_RETURN_STR(BTIF_MEDIA_FLUSH_AA_TX)
+ CASE_RETURN_STR(BTIF_MEDIA_FLUSH_AA_RX)
+ CASE_RETURN_STR(BTIF_MEDIA_AUDIO_FEEDING_INIT)
+ CASE_RETURN_STR(BTIF_MEDIA_AUDIO_RECEIVING_INIT)
+ CASE_RETURN_STR(BTIF_MEDIA_AUDIO_SINK_CFG_UPDATE)
+ CASE_RETURN_STR(BTIF_MEDIA_AUDIO_SINK_CLEAR_TRACK)
+
+ default:
+ return "UNKNOWN MEDIA EVENT";
+ }
+}
+
+/*****************************************************************************
+ ** A2DP CTRL PATH
+ *****************************************************************************/
+#if 0
+// TODO: consider the necessity to add an API based on this function
+static void btif_audiopath_detached(void)
+{
+ APPL_TRACE_EVENT("## AUDIO PATH DETACHED ##");
+
+ /* send stop request only if we are actively streaming and haven't received
+ a stop request. Potentially audioflinger detached abnormally */
+ if (btif_media_cb.is_tx_timer) {
+ /* post stop event and wait for audio path to stop */
+ btif_dispatch_sm_event(BTIF_AV_STOP_STREAM_REQ_EVT, NULL, 0);
+ }
+}
+#endif
+
+/*****************************************************************************
+ ** BTIF ADAPTATION
+ *****************************************************************************/
+
+static void btif_media_task_post(uint32_t sig)
+{
+ BtTaskEvt_t *evt = (BtTaskEvt_t *)osi_malloc(sizeof(BtTaskEvt_t));
+ if (evt == NULL) {
+ return;
+ }
+
+ evt->sig = sig;
+ evt->par = 0;
+
+ if (xQueueSend(xBtifMediaQueue, &evt, 10 / portTICK_RATE_MS) != pdTRUE) {
+ APPL_TRACE_ERROR("xBtifMediaQueue failed\n");
+ }
+}
+
+static void btif_media_task_handler(void *arg)
+{
+ BtTaskEvt_t *e;
+ for (;;) {
+ if (pdTRUE == xQueueReceive(xBtifMediaQueue, &e, (portTickType)portMAX_DELAY)) {
+ // LOG_ERROR("med evt %d\n", e->sig);
+ switch (e->sig) {
+ case SIG_MEDIA_TASK_AVK_ALARM_TO:
+ btif_media_task_avk_handle_timer(NULL);
+ break;
+ case SIG_MEDIA_TASK_CMD_READY:
+ fixed_queue_process(btif_media_cmd_msg_queue);
+ break;
+ case SIG_MEDIA_TASK_INIT:
+ btif_media_thread_init(NULL);
+ break;
+ case SIG_MEDIA_TASK_CLEAN_UP:
+ btif_media_thread_cleanup(NULL);
+ break;
+ default:
+ APPL_TRACE_ERROR("media task unhandled evt: 0x%x\n", e->sig);
+ }
+ }
+ osi_free(e);
+ }
+}
+
+bool btif_a2dp_start_media_task(void)
+{
+ if (media_task_running != MEDIA_TASK_STATE_OFF) {
+ APPL_TRACE_ERROR("warning : media task already running");
+ return false;
+ }
+
+ APPL_TRACE_EVENT("## A2DP START MEDIA THREAD ##");
+
+ btif_media_cmd_msg_queue = fixed_queue_new(SIZE_MAX);
+ if (btif_media_cmd_msg_queue == NULL) {
+ goto error_exit;
+ }
+
+ xBtifMediaQueue = xQueueCreate(60, sizeof(void *));
+ if (xBtifMediaQueue == 0) {
+ goto error_exit;
+ }
++ xTaskCreatePinnedToCore(btif_media_task_handler, "BtifMediaT\n", 2048, NULL, configMAX_PRIORITIES - 1, &xBtifMediaTaskHandle, 0);
+ if (xBtifMediaTaskHandle == NULL) {
+ goto error_exit;
+ }
+ fixed_queue_register_dequeue(btif_media_cmd_msg_queue, btif_media_thread_handle_cmd);
+ btif_media_task_post(SIG_MEDIA_TASK_INIT);
+
+ APPL_TRACE_EVENT("## A2DP MEDIA THREAD STARTED ##\n");
+
+ return true;
+
+error_exit:;
+ APPL_TRACE_ERROR("%s unable to start up media thread\n", __func__);
+
+ if (xBtifMediaTaskHandle != NULL) {
+ vTaskDelete(xBtifMediaTaskHandle);
+ xBtifMediaTaskHandle = NULL;
+ }
+
+ if (xBtifMediaQueue != 0) {
+ vQueueDelete(xBtifMediaQueue);
+ xBtifMediaQueue = 0;
+ }
+ fixed_queue_free(btif_media_cmd_msg_queue, NULL);
+ btif_media_cmd_msg_queue = NULL;
+ return false;
+}
+
+void btif_a2dp_stop_media_task(void)
+{
+ APPL_TRACE_EVENT("## A2DP STOP MEDIA THREAD ##\n");
+
+ // Exit thread
+ btif_media_task_post(SIG_MEDIA_TASK_CLEAN_UP);
+ // TODO: wait until CLEAN up is done, then do task delete
+ vTaskDelete(xBtifMediaTaskHandle);
+ xBtifMediaTaskHandle = NULL;
+ vQueueDelete(xBtifMediaQueue);
+ xBtifMediaQueue = NULL;
+
+ fixed_queue_free(btif_media_cmd_msg_queue, NULL);
+ btif_media_cmd_msg_queue = NULL;
+}
+
+/*****************************************************************************
+**
+** Function btif_a2dp_on_init
+**
+** Description
+**
+** Returns
+**
+*******************************************************************************/
+
+void btif_a2dp_on_init(void)
+{
+ //tput_mon(1, 0, 1);
+}
+
+
+/*****************************************************************************
+**
+** Function btif_a2dp_setup_codec
+**
+** Description
+**
+** Returns
+**
+*******************************************************************************/
+
+void btif_a2dp_setup_codec(void)
+{
+ tBTIF_AV_MEDIA_FEEDINGS media_feeding;
+ tBTIF_STATUS status;
+
+ APPL_TRACE_EVENT("## A2DP SETUP CODEC ##\n");
+
+ GKI_disable();
+
+ /* for now hardcode 44.1 khz 16 bit stereo PCM format */
+ media_feeding.cfg.pcm.sampling_freq = 44100;
+ media_feeding.cfg.pcm.bit_per_sample = 16;
+ media_feeding.cfg.pcm.num_channel = 2;
+ media_feeding.format = BTIF_AV_CODEC_PCM;
+
+ bta_av_co_audio_set_codec(&media_feeding, &status);
+
+ GKI_enable();
+}
+
+
+/*****************************************************************************
+**
+** Function btif_a2dp_on_idle
+**
+** Description
+**
+** Returns
+**
+*******************************************************************************/
+
+void btif_a2dp_on_idle(void)
+{
+ APPL_TRACE_EVENT("## ON A2DP IDLE ##\n");
+
+ bta_av_co_init();
+#if (BTA_AV_SINK_INCLUDED == TRUE)
+ if (btif_media_cb.peer_sep == AVDT_TSEP_SRC) {
+ btif_media_cb.rx_flush = TRUE;
+ btif_media_task_aa_rx_flush_req();
+ btif_media_task_aa_handle_stop_decoding();
+ btif_media_task_clear_track();
+ APPL_TRACE_DEBUG("Stopped BT track");
+ }
+#endif
+}
+
+/*******************************************************************************
+ **
+ ** Function btif_media_task_clear_track
+ **
+ ** Description
+ **
+ ** Returns TRUE is success
+ **
+ *******************************************************************************/
+BOOLEAN btif_media_task_clear_track(void)
+{
+ BT_HDR *p_buf;
+
+ if (NULL == (p_buf = GKI_getbuf(sizeof(BT_HDR)))) {
+ return FALSE;
+ }
+
+ p_buf->event = BTIF_MEDIA_AUDIO_SINK_CLEAR_TRACK;
+
+ fixed_queue_enqueue(btif_media_cmd_msg_queue, p_buf);
+ btif_media_task_post(SIG_MEDIA_TASK_CMD_READY);
+ return TRUE;
+}
+
+/*****************************************************************************
+**
+** Function btif_reset_decoder
+**
+** Description
+**
+** Returns
+**
+*******************************************************************************/
+
+void btif_reset_decoder(UINT8 *p_av)
+{
+ APPL_TRACE_EVENT("btif_reset_decoder");
+ APPL_TRACE_DEBUG("btif_reset_decoder p_codec_info[%x:%x:%x:%x:%x:%x]\n",
+ p_av[1], p_av[2], p_av[3],
+ p_av[4], p_av[5], p_av[6]);
+
+ tBTIF_MEDIA_SINK_CFG_UPDATE *p_buf;
+ if (NULL == (p_buf = GKI_getbuf(sizeof(tBTIF_MEDIA_SINK_CFG_UPDATE)))) {
+ APPL_TRACE_ERROR("btif_reset_decoder No Buffer ");
+ return;
+ }
+
+ memcpy(p_buf->codec_info, p_av, AVDT_CODEC_SIZE);
+ p_buf->hdr.event = BTIF_MEDIA_AUDIO_SINK_CFG_UPDATE;
+
+ fixed_queue_enqueue(btif_media_cmd_msg_queue, p_buf);
+ btif_media_task_post(SIG_MEDIA_TASK_CMD_READY);
+}
+
+/*****************************************************************************
+**
+** Function btif_a2dp_on_stopped
+**
+** Description
+**
+** Returns
+**
+*******************************************************************************/
+
+void btif_a2dp_on_stopped(tBTA_AV_SUSPEND *p_av)
+{
+ APPL_TRACE_EVENT("## ON A2DP STOPPED ##\n");
+ if (btif_media_cb.peer_sep == AVDT_TSEP_SRC) { /* Handling for A2DP SINK cases*/
+ btif_media_cb.rx_flush = TRUE;
+ btif_media_task_aa_rx_flush_req();
+ btif_media_task_aa_handle_stop_decoding();
+ btif_media_cb.data_channel_open = FALSE;
+ return;
+ }
+}
+
+
+/*****************************************************************************
+**
+** Function btif_a2dp_on_suspended
+**
+** Description
+**
+** Returns
+**
+*******************************************************************************/
+
+void btif_a2dp_on_suspended(tBTA_AV_SUSPEND *p_av)
+{
+ APPL_TRACE_EVENT("## ON A2DP SUSPENDED ##\n");
+ if (btif_media_cb.peer_sep == AVDT_TSEP_SRC) {
+ btif_media_cb.rx_flush = TRUE;
+ btif_media_task_aa_rx_flush_req();
+ btif_media_task_aa_handle_stop_decoding();
+ return;
+ }
+}
+
+/* when true media task discards any rx frames */
+void btif_a2dp_set_rx_flush(BOOLEAN enable)
+{
+ APPL_TRACE_EVENT("## DROP RX %d ##\n", enable);
+ btif_media_cb.rx_flush = enable;
+}
+
+#if (BTA_AV_SINK_INCLUDED == TRUE)
+static void btif_media_task_avk_handle_timer(UNUSED_ATTR void *context)
+{
+ UINT8 count;
+ tBT_SBC_HDR *p_msg;
+ int num_sbc_frames;
+ int num_frames_to_process;
+
+ count = btif_media_cb.RxSbcQ._count;
+ if (0 == count) {
+ APPL_TRACE_DEBUG(" QUE EMPTY ");
+ } else {
+ if (btif_media_cb.rx_flush == TRUE) {
+ btif_media_flush_q(&(btif_media_cb.RxSbcQ));
+ return;
+ }
+
+ num_frames_to_process = btif_media_cb.frames_to_process;
+ APPL_TRACE_DEBUG(" Process Frames + ");
+ do {
+ p_msg = (tBT_SBC_HDR *)GKI_getfirst(&(btif_media_cb.RxSbcQ));
+ if (p_msg == NULL) {
+ return;
+ }
+ num_sbc_frames = p_msg->num_frames_to_be_processed; /* num of frames in Que Packets */
+ APPL_TRACE_DEBUG(" Frames left in topmost packet %d\n", num_sbc_frames);
+ APPL_TRACE_DEBUG(" Remaining frames to process in tick %d\n", num_frames_to_process);
+ APPL_TRACE_DEBUG(" Num of Packets in Que %d\n", btif_media_cb.RxSbcQ._count);
+
+ if ( num_sbc_frames > num_frames_to_process) { /* Que Packet has more frames*/
+ p_msg->num_frames_to_be_processed = num_frames_to_process;
+ btif_media_task_handle_inc_media(p_msg);
+ p_msg->num_frames_to_be_processed = num_sbc_frames - num_frames_to_process;
+ num_frames_to_process = 0;
+ break;
+ } else { /* Que packet has less frames */
+ btif_media_task_handle_inc_media(p_msg);
+ p_msg = (tBT_SBC_HDR *)GKI_dequeue(&(btif_media_cb.RxSbcQ));
+ if ( p_msg == NULL ) {
+ APPL_TRACE_ERROR("Insufficient data in que ");
+ break;
+ }
+ num_frames_to_process = num_frames_to_process - p_msg->num_frames_to_be_processed;
+ GKI_freebuf(p_msg);
+ }
+ } while (num_frames_to_process > 0);
+
+ APPL_TRACE_DEBUG(" Process Frames - ");
+ }
+}
+#else
+static void btif_media_task_avk_handle_timer(UNUSED_ATTR void *context) {}
+#endif
+
+static void btif_media_thread_init(UNUSED_ATTR void *context)
+{
+ memset(&btif_media_cb, 0, sizeof(btif_media_cb));
+ LOG_INFO("media thread init\n");
+ btif_media_cb.av_sm_hdl = btif_av_get_sm_handle();
+ raise_priority_a2dp(TASK_HIGH_MEDIA);
+ media_task_running = MEDIA_TASK_STATE_ON;
+}
+
+static void btif_media_thread_cleanup(UNUSED_ATTR void *context)
+{
+ /* make sure no channels are restarted while shutting down */
+ media_task_running = MEDIA_TASK_STATE_SHUTTING_DOWN;
+
+ btif_media_cb.data_channel_open = FALSE;
+ /* Clear media task flag */
+ media_task_running = MEDIA_TASK_STATE_OFF;
+}
+
+/*******************************************************************************
+ **
+ ** Function btif_media_flush_q
+ **
+ ** Description
+ **
+ ** Returns void
+ **
+ *******************************************************************************/
+static void btif_media_flush_q(BUFFER_Q *p_q)
+{
+ while (!GKI_queue_is_empty(p_q)) {
+ GKI_freebuf(GKI_dequeue(p_q));
+ }
+}
+
+static void btif_media_thread_handle_cmd(fixed_queue_t *queue)
+{
+ BT_HDR *p_msg;
+ while (!fixed_queue_is_empty(queue)) {
+ p_msg = (BT_HDR *)fixed_queue_dequeue(queue);
+ LOG_VERBOSE("btif_media_thread_handle_cmd : %d %s\n", p_msg->event,
+ dump_media_event(p_msg->event));
+
+ switch (p_msg->event) {
+#if (BTA_AV_INCLUDED == TRUE)
+ case BTIF_MEDIA_AUDIO_SINK_CFG_UPDATE:
+#if (BTA_AV_SINK_INCLUDED == TRUE)
+ btif_media_task_aa_handle_decoder_reset(p_msg);
+#endif
+ break;
+ case BTIF_MEDIA_AUDIO_SINK_CLEAR_TRACK:
+#if (BTA_AV_SINK_INCLUDED == TRUE)
+ btif_media_task_aa_handle_clear_track();
+#endif
+ break;
+ case BTIF_MEDIA_FLUSH_AA_RX:
+ btif_media_task_aa_rx_flush();
+ break;
+#endif
+ default:
+ APPL_TRACE_ERROR("ERROR in %s unknown event %d\n", __func__, p_msg->event);
+ }
+ GKI_freebuf(p_msg);
+ LOG_VERBOSE("%s: %s DONE\n", __func__, dump_media_event(p_msg->event));
+ }
+}
+
+
+#if (BTA_AV_SINK_INCLUDED == TRUE)
+/*******************************************************************************
+ **
+ ** Function btif_media_task_handle_inc_media
+ **
+ ** Description
+ **
+ ** Returns void
+ **
+ *******************************************************************************/
+static void btif_media_task_handle_inc_media(tBT_SBC_HDR *p_msg)
+{
+ UINT8 *sbc_start_frame = ((UINT8 *)(p_msg + 1) + p_msg->offset + 1);
+ int count;
+ UINT32 pcmBytes, availPcmBytes;
+ OI_INT16 *pcmDataPointer = pcmData; /*Will be overwritten on next packet receipt*/
+ OI_STATUS status;
+ int num_sbc_frames = p_msg->num_frames_to_be_processed;
+ UINT32 sbc_frame_len = p_msg->len - 1;
+ availPcmBytes = 2 * sizeof(pcmData);
+
+ if ((btif_media_cb.peer_sep == AVDT_TSEP_SNK) || (btif_media_cb.rx_flush)) {
+ APPL_TRACE_DEBUG(" State Changed happened in this tick ");
+ return;
+ }
+
+ // ignore data if no one is listening
+ if (!btif_media_cb.data_channel_open) {
+ return;
+ }
+
+ APPL_TRACE_DEBUG("Number of sbc frames %d, frame_len %d\n", num_sbc_frames, sbc_frame_len);
+ // LOG_ERROR("Number of sbc frames %d, frame_len %d\n", num_sbc_frames, sbc_frame_len);
+ for (count = 0; count < num_sbc_frames && sbc_frame_len != 0; count ++) {
+ pcmBytes = availPcmBytes;
+ status = OI_CODEC_SBC_DecodeFrame(&context, (const OI_BYTE **)&sbc_start_frame,
+ (OI_UINT32 *)&sbc_frame_len,
+ (OI_INT16 *)pcmDataPointer,
+ (OI_UINT32 *)&pcmBytes);
+ if (!OI_SUCCESS(status)) {
+ APPL_TRACE_ERROR("Decoding failure: %d\n", status);
+ break;
+ }
+ availPcmBytes -= pcmBytes;
+ pcmDataPointer += pcmBytes / 2;
+ p_msg->offset += (p_msg->len - 1) - sbc_frame_len;
+ p_msg->len = sbc_frame_len + 1;
+ }
+ // LOG_ERROR("pre-send: %d\n", availPcmBytes);
+
+ // UIPC_Send(UIPC_CH_ID_AV_AUDIO, 0, (UINT8 *)pcmData, (2 * sizeof(pcmData) - availPcmBytes));
+ BTIF_A2D_DATA_CB_TO_APP((const uint8_t *)pcmData, (2 * sizeof(pcmData) - availPcmBytes));
+
+}
+#endif
+
+#if (BTA_AV_INCLUDED == TRUE)
+
+/*******************************************************************************
+ **
+ ** Function btif_media_task_aa_rx_flush_req
+ **
+ ** Description
+ **
+ ** Returns TRUE is success
+ **
+ *******************************************************************************/
+BOOLEAN btif_media_task_aa_rx_flush_req(void)
+{
+ BT_HDR *p_buf;
+
+ if (GKI_queue_is_empty(&(btif_media_cb.RxSbcQ)) == TRUE) { /* Que is already empty */
+ return TRUE;
+ }
+
+ if (NULL == (p_buf = GKI_getbuf(sizeof(BT_HDR)))) {
+ return FALSE;
+ }
+
+ p_buf->event = BTIF_MEDIA_FLUSH_AA_RX;
+
+ fixed_queue_enqueue(btif_media_cmd_msg_queue, p_buf);
+ btif_media_task_post(SIG_MEDIA_TASK_CMD_READY);
+ return TRUE;
+}
+
+/*******************************************************************************
+ **
+ ** Function btif_media_task_aa_rx_flush
+ **
+ ** Description
+ **
+ ** Returns void
+ **
+ *******************************************************************************/
+static void btif_media_task_aa_rx_flush(void)
+{
+ /* Flush all enqueued GKI SBC buffers (encoded) */
+ APPL_TRACE_DEBUG("btif_media_task_aa_rx_flush");
+
+ btif_media_flush_q(&(btif_media_cb.RxSbcQ));
+}
+
+int btif_a2dp_get_track_frequency(UINT8 frequency)
+{
+ int freq = 48000;
+ switch (frequency) {
+ case A2D_SBC_IE_SAMP_FREQ_16:
+ freq = 16000;
+ break;
+ case A2D_SBC_IE_SAMP_FREQ_32:
+ freq = 32000;
+ break;
+ case A2D_SBC_IE_SAMP_FREQ_44:
+ freq = 44100;
+ break;
+ case A2D_SBC_IE_SAMP_FREQ_48:
+ freq = 48000;
+ break;
+ }
+ return freq;
+}
+
+int btif_a2dp_get_track_channel_count(UINT8 channeltype)
+{
+ int count = 1;
+ switch (channeltype) {
+ case A2D_SBC_IE_CH_MD_MONO:
+ count = 1;
+ break;
+ case A2D_SBC_IE_CH_MD_DUAL:
+ case A2D_SBC_IE_CH_MD_STEREO:
+ case A2D_SBC_IE_CH_MD_JOINT:
+ count = 2;
+ break;
+ }
+ return count;
+}
+
+void btif_a2dp_set_peer_sep(UINT8 sep)
+{
+ btif_media_cb.peer_sep = sep;
+}
+
+static void btif_decode_alarm_cb(UNUSED_ATTR void *context)
+{
+ btif_media_task_post(SIG_MEDIA_TASK_AVK_ALARM_TO);
+}
+
+static void btif_media_task_aa_handle_stop_decoding(void)
+{
+ osi_alarm_free(btif_media_cb.decode_alarm);
+ btif_media_cb.decode_alarm = NULL;
+}
+
+static void btif_media_task_aa_handle_start_decoding(void)
+{
+ if (btif_media_cb.decode_alarm) {
+ return;
+ }
+
+ btif_media_cb.decode_alarm = osi_alarm_new("dec_timer\n", btif_decode_alarm_cb, NULL, BTIF_SINK_MEDIA_TIME_TICK, true);
+ if (!btif_media_cb.decode_alarm) {
+ APPL_TRACE_ERROR("%s unable to allocate decode alarm.\n", __func__);
+ return;
+ }
+ osi_alarm_set(btif_media_cb.decode_alarm, BTIF_SINK_MEDIA_TIME_TICK);
+}
+
+#if (BTA_AV_SINK_INCLUDED == TRUE)
+
+static void btif_media_task_aa_handle_clear_track (void)
+{
+ APPL_TRACE_DEBUG("btif_media_task_aa_handle_clear_track");
+}
+
+/*******************************************************************************
+ **
+ ** Function btif_media_task_aa_handle_decoder_reset
+ **
+ ** Description
+ **
+ ** Returns void
+ **
+ *******************************************************************************/
+static void btif_media_task_aa_handle_decoder_reset(BT_HDR *p_msg)
+{
+ tBTIF_MEDIA_SINK_CFG_UPDATE *p_buf = (tBTIF_MEDIA_SINK_CFG_UPDATE *) p_msg;
+ tA2D_STATUS a2d_status;
+ tA2D_SBC_CIE sbc_cie;
+ OI_STATUS status;
+ UINT32 freq_multiple = 48 * 20; /* frequency multiple for 20ms of data , initialize with 48K*/
+ UINT32 num_blocks = 16;
+ UINT32 num_subbands = 8;
+
+ APPL_TRACE_EVENT("btif_media_task_aa_handle_decoder_reset p_codec_info[%x:%x:%x:%x:%x:%x]\n",
+ p_buf->codec_info[1], p_buf->codec_info[2], p_buf->codec_info[3],
+ p_buf->codec_info[4], p_buf->codec_info[5], p_buf->codec_info[6]);
+
+ a2d_status = A2D_ParsSbcInfo(&sbc_cie, p_buf->codec_info, FALSE);
+ if (a2d_status != A2D_SUCCESS) {
+ APPL_TRACE_ERROR("ERROR dump_codec_info A2D_ParsSbcInfo fail:%d\n", a2d_status);
+ return;
+ }
+
+ btif_media_cb.sample_rate = btif_a2dp_get_track_frequency(sbc_cie.samp_freq);
+ btif_media_cb.channel_count = btif_a2dp_get_track_channel_count(sbc_cie.ch_mode);
+
+ btif_media_cb.rx_flush = FALSE;
+ APPL_TRACE_EVENT("Reset to sink role");
+ status = OI_CODEC_SBC_DecoderReset(&context, contextData, sizeof(contextData), 2, 2, FALSE);
+ if (!OI_SUCCESS(status)) {
+ APPL_TRACE_ERROR("OI_CODEC_SBC_DecoderReset failed with error code %d\n", status);
+ }
+
+ btif_media_cb.data_channel_open = TRUE;
+
+ switch (sbc_cie.samp_freq) {
+ case A2D_SBC_IE_SAMP_FREQ_16:
+ APPL_TRACE_DEBUG("\tsamp_freq:%d (16000)\n", sbc_cie.samp_freq);
+ freq_multiple = 16 * 20;
+ break;
+ case A2D_SBC_IE_SAMP_FREQ_32:
+ APPL_TRACE_DEBUG("\tsamp_freq:%d (32000)\n", sbc_cie.samp_freq);
+ freq_multiple = 32 * 20;
+ break;
+ case A2D_SBC_IE_SAMP_FREQ_44:
+ APPL_TRACE_DEBUG("\tsamp_freq:%d (44100)\n", sbc_cie.samp_freq);
+ freq_multiple = 441 * 2;
+ break;
+ case A2D_SBC_IE_SAMP_FREQ_48:
+ APPL_TRACE_DEBUG("\tsamp_freq:%d (48000)\n", sbc_cie.samp_freq);
+ freq_multiple = 48 * 20;
+ break;
+ default:
+ APPL_TRACE_DEBUG(" Unknown Frequency ");
+ break;
+ }
+
+ switch (sbc_cie.ch_mode) {
+ case A2D_SBC_IE_CH_MD_MONO:
+ APPL_TRACE_DEBUG("\tch_mode:%d (Mono)\n", sbc_cie.ch_mode);
+ break;
+ case A2D_SBC_IE_CH_MD_DUAL:
+ APPL_TRACE_DEBUG("\tch_mode:%d (DUAL)\n", sbc_cie.ch_mode);
+ break;
+ case A2D_SBC_IE_CH_MD_STEREO:
+ APPL_TRACE_DEBUG("\tch_mode:%d (STEREO)\n", sbc_cie.ch_mode);
+ break;
+ case A2D_SBC_IE_CH_MD_JOINT:
+ APPL_TRACE_DEBUG("\tch_mode:%d (JOINT)\n", sbc_cie.ch_mode);
+ break;
+ default:
+ APPL_TRACE_DEBUG(" Unknown Mode ");
+ break;
+ }
+
+ switch (sbc_cie.block_len) {
+ case A2D_SBC_IE_BLOCKS_4:
+ APPL_TRACE_DEBUG("\tblock_len:%d (4)\n", sbc_cie.block_len);
+ num_blocks = 4;
+ break;
+ case A2D_SBC_IE_BLOCKS_8:
+ APPL_TRACE_DEBUG("\tblock_len:%d (8)\n", sbc_cie.block_len);
+ num_blocks = 8;
+ break;
+ case A2D_SBC_IE_BLOCKS_12:
+ APPL_TRACE_DEBUG("\tblock_len:%d (12)\n", sbc_cie.block_len);
+ num_blocks = 12;
+ break;
+ case A2D_SBC_IE_BLOCKS_16:
+ APPL_TRACE_DEBUG("\tblock_len:%d (16)\n", sbc_cie.block_len);
+ num_blocks = 16;
+ break;
+ default:
+ APPL_TRACE_DEBUG(" Unknown BlockLen ");
+ break;
+ }
+
+ switch (sbc_cie.num_subbands) {
+ case A2D_SBC_IE_SUBBAND_4:
+ APPL_TRACE_DEBUG("\tnum_subbands:%d (4)\n", sbc_cie.num_subbands);
+ num_subbands = 4;
+ break;
+ case A2D_SBC_IE_SUBBAND_8:
+ APPL_TRACE_DEBUG("\tnum_subbands:%d (8)\n", sbc_cie.num_subbands);
+ num_subbands = 8;
+ break;
+ default:
+ APPL_TRACE_DEBUG(" Unknown SubBands ");
+ break;
+ }
+
+ switch (sbc_cie.alloc_mthd) {
+ case A2D_SBC_IE_ALLOC_MD_S:
+ APPL_TRACE_DEBUG("\talloc_mthd:%d (SNR)\n", sbc_cie.alloc_mthd);
+ break;
+ case A2D_SBC_IE_ALLOC_MD_L:
+ APPL_TRACE_DEBUG("\talloc_mthd:%d (Loudness)\n", sbc_cie.alloc_mthd);
+ break;
+ default:
+ APPL_TRACE_DEBUG(" Unknown Allocation Method");
+ break;
+ }
+
+ APPL_TRACE_EVENT("\tBit pool Min:%d Max:%d\n", sbc_cie.min_bitpool, sbc_cie.max_bitpool);
+
+ btif_media_cb.frames_to_process = ((freq_multiple) / (num_blocks * num_subbands)) + 1;
+ APPL_TRACE_EVENT(" Frames to be processed in 20 ms %d\n", btif_media_cb.frames_to_process);
+}
+#endif
+
+/*******************************************************************************
+ **
+ ** Function btif_media_sink_enque_buf
+ **
+ ** Description This function is called by the av_co to fill A2DP Sink Queue
+ **
+ **
+ ** Returns size of the queue
+ *******************************************************************************/
+UINT8 btif_media_sink_enque_buf(BT_HDR *p_pkt)
+{
+ tBT_SBC_HDR *p_msg;
+
+ if (btif_media_cb.rx_flush == TRUE) { /* Flush enabled, do not enque*/
+ return GKI_queue_length(&btif_media_cb.RxSbcQ);
+ }
+ if (GKI_queue_length(&btif_media_cb.RxSbcQ) == MAX_OUTPUT_A2DP_FRAME_QUEUE_SZ) {
+ GKI_freebuf(GKI_dequeue(&(btif_media_cb.RxSbcQ)));
+ }
+
+ BTIF_TRACE_VERBOSE("btif_media_sink_enque_buf + ");
+
+ /* allocate and Queue this buffer */
+ if ((p_msg = (tBT_SBC_HDR *) GKI_getbuf(sizeof(tBT_SBC_HDR) +
+ p_pkt->offset + p_pkt->len)) != NULL) {
+ memcpy(p_msg, p_pkt, (sizeof(BT_HDR) + p_pkt->offset + p_pkt->len));
+ p_msg->num_frames_to_be_processed = (*((UINT8 *)(p_msg + 1) + p_msg->offset)) & 0x0f;
+ BTIF_TRACE_VERBOSE("btif_media_sink_enque_buf %d + \n", p_msg->num_frames_to_be_processed);
+ // LOG_ERROR("sink enq %d\n", p_msg->num_frames_to_be_processed);
+ GKI_enqueue(&(btif_media_cb.RxSbcQ), p_msg);
+ if (GKI_queue_length(&btif_media_cb.RxSbcQ) == MAX_A2DP_DELAYED_START_FRAME_COUNT) {
+ BTIF_TRACE_DEBUG(" Initiate Decoding ");
+ btif_media_task_aa_handle_start_decoding();
+ }
+ } else {
+ /* let caller deal with a failed allocation */
+ BTIF_TRACE_VERBOSE("btif_media_sink_enque_buf No Buffer left - ");
+ }
+ return GKI_queue_length(&btif_media_cb.RxSbcQ);
+}
+
+
+/*******************************************************************************
+ **
+ ** Function btif_media_aa_readbuf
+ **
+ ** Description This function is called by the av_co to get the next buffer to send
+ **
+ **
+ ** Returns void
+ *******************************************************************************/
+BT_HDR *btif_media_aa_readbuf(void)
+{
+ return NULL;
+}
+
+#endif /* BTA_AV_INCLUDED == TRUE */
+/*******************************************************************************
+ **
+ ** Function dump_codec_info
+ **
+ ** Description Decode and display codec_info (for debug)
+ **
+ ** Returns void
+ **
+ *******************************************************************************/
+void dump_codec_info(unsigned char *p_codec)
+{
+ tA2D_STATUS a2d_status;
+ tA2D_SBC_CIE sbc_cie;
+
+ a2d_status = A2D_ParsSbcInfo(&sbc_cie, p_codec, FALSE);
+ if (a2d_status != A2D_SUCCESS) {
+ APPL_TRACE_ERROR("ERROR dump_codec_info A2D_ParsSbcInfo fail:%d\n", a2d_status);
+ return;
+ }
+
+ APPL_TRACE_DEBUG("dump_codec_info");
+
+ if (sbc_cie.samp_freq == A2D_SBC_IE_SAMP_FREQ_16) {
+ APPL_TRACE_DEBUG("\tsamp_freq:%d (16000)\n", sbc_cie.samp_freq);
+ } else if (sbc_cie.samp_freq == A2D_SBC_IE_SAMP_FREQ_32) {
+ APPL_TRACE_DEBUG("\tsamp_freq:%d (32000)\n", sbc_cie.samp_freq);
+ } else if (sbc_cie.samp_freq == A2D_SBC_IE_SAMP_FREQ_44) {
+ APPL_TRACE_DEBUG("\tsamp_freq:%d (44.100)\n", sbc_cie.samp_freq);
+ } else if (sbc_cie.samp_freq == A2D_SBC_IE_SAMP_FREQ_48) {
+ APPL_TRACE_DEBUG("\tsamp_freq:%d (48000)\n", sbc_cie.samp_freq);
+ } else {
+ APPL_TRACE_DEBUG("\tBAD samp_freq:%d\n", sbc_cie.samp_freq);
+ }
+
+ if (sbc_cie.ch_mode == A2D_SBC_IE_CH_MD_MONO) {
+ APPL_TRACE_DEBUG("\tch_mode:%d (Mono)\n", sbc_cie.ch_mode);
+ } else if (sbc_cie.ch_mode == A2D_SBC_IE_CH_MD_DUAL) {
+ APPL_TRACE_DEBUG("\tch_mode:%d (Dual)\n", sbc_cie.ch_mode);
+ } else if (sbc_cie.ch_mode == A2D_SBC_IE_CH_MD_STEREO) {
+ APPL_TRACE_DEBUG("\tch_mode:%d (Stereo)\n", sbc_cie.ch_mode);
+ } else if (sbc_cie.ch_mode == A2D_SBC_IE_CH_MD_JOINT) {
+ APPL_TRACE_DEBUG("\tch_mode:%d (Joint)\n", sbc_cie.ch_mode);
+ } else {
+ APPL_TRACE_DEBUG("\tBAD ch_mode:%d\n", sbc_cie.ch_mode);
+ }
+
+ if (sbc_cie.block_len == A2D_SBC_IE_BLOCKS_4) {
+ APPL_TRACE_DEBUG("\tblock_len:%d (4)\n", sbc_cie.block_len);
+ } else if (sbc_cie.block_len == A2D_SBC_IE_BLOCKS_8) {
+ APPL_TRACE_DEBUG("\tblock_len:%d (8)\n", sbc_cie.block_len);
+ } else if (sbc_cie.block_len == A2D_SBC_IE_BLOCKS_12) {
+ APPL_TRACE_DEBUG("\tblock_len:%d (12)\n", sbc_cie.block_len);
+ } else if (sbc_cie.block_len == A2D_SBC_IE_BLOCKS_16) {
+ APPL_TRACE_DEBUG("\tblock_len:%d (16)\n", sbc_cie.block_len);
+ } else {
+ APPL_TRACE_DEBUG("\tBAD block_len:%d\n", sbc_cie.block_len);
+ }
+
+ if (sbc_cie.num_subbands == A2D_SBC_IE_SUBBAND_4) {
+ APPL_TRACE_DEBUG("\tnum_subbands:%d (4)\n", sbc_cie.num_subbands);
+ } else if (sbc_cie.num_subbands == A2D_SBC_IE_SUBBAND_8) {
+ APPL_TRACE_DEBUG("\tnum_subbands:%d (8)\n", sbc_cie.num_subbands);
+ } else {
+ APPL_TRACE_DEBUG("\tBAD num_subbands:%d\n", sbc_cie.num_subbands);
+ }
+
+ if (sbc_cie.alloc_mthd == A2D_SBC_IE_ALLOC_MD_S) {
+ APPL_TRACE_DEBUG("\talloc_mthd:%d (SNR)\n", sbc_cie.alloc_mthd);
+ } else if (sbc_cie.alloc_mthd == A2D_SBC_IE_ALLOC_MD_L) {
+ APPL_TRACE_DEBUG("\talloc_mthd:%d (Loundess)\n", sbc_cie.alloc_mthd);
+ } else {
+ APPL_TRACE_DEBUG("\tBAD alloc_mthd:%d\n", sbc_cie.alloc_mthd);
+ }
+
+ APPL_TRACE_DEBUG("\tBit pool Min:%d Max:%d\n", sbc_cie.min_bitpool, sbc_cie.max_bitpool);
+
+}
hci_hal_env_init(HCI_HAL_SERIAL_BUFFER_SIZE, SIZE_MAX);
xHciH4Queue = xQueueCreate(HCI_H4_QUEUE_NUM, sizeof(BtTaskEvt_t));
- xTaskCreate(hci_hal_h4_rx_handler, HCI_H4_TASK_NAME, HCI_H4_TASK_STACK_SIZE, NULL, HCI_H4_TASK_PRIO, &xHciH4TaskHandle);
+ xTaskCreatePinnedToCore(hci_hal_h4_rx_handler, HCI_H4_TASK_NAME, HCI_H4_TASK_STACK_SIZE, NULL, HCI_H4_TASK_PRIO, &xHciH4TaskHandle, 0);
//register vhci host cb
- API_vhci_host_register_callback(&vhci_host_cb);
+ esp_vhci_host_register_callback(&vhci_host_cb);
-
return true;
}
*/
BtTaskEvt_t e;
-
for (;;) {
if (pdTRUE == xQueueReceive(xHciHostQueue, &e, (portTickType)portMAX_DELAY)) {
-
if (e.sig == 0xff) {
- if (API_vhci_host_check_send_available()) {
+ if (esp_vhci_host_check_send_available()) {
/*Now Target only allowed one packet per TX*/
BT_HDR *pkt = packet_fragmenter->fragment_current_packet();
if (pkt != NULL) {
/* invalid connection ID
*/
- #define GATT_INVALID_CONN_ID 0xFFFF
+ #define GATT_INVALID_CONN_ID 0xFFFF
#ifndef GATT_CL_MAX_LCB
- #define GATT_CL_MAX_LCB 4 // 22
-#define GATT_CL_MAX_LCB 12 // 22
++#define GATT_CL_MAX_LCB 4 // 22
#endif
#ifndef GATT_MAX_SCCB
- #define GATT_MAX_SCCB 4
-#define GATT_MAX_SCCB 10
++#define GATT_MAX_SCCB 4
#endif
--- /dev/null
- #include "EspAudio.h"
- #include "EspAudioCom.h"
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+#include "freertos/timers.h"
+
+#include "esp_system.h"
- static void key_tmr_handler(TimerHandle_t xTimer)
- {
- if (m_audio_state != ESP_A2D_AUDIO_STATE_STARTED) {
- BT_APP_TRACE_EVENT("-----key_tmr_hdlr, return, audio state: %d\n", m_audio_state);
- return;
- }
-
- bt_app_evt_arg param;
- memset(¶m, 0, sizeof(bt_app_evt_arg));
- if (m_key_state == 1) {
- param.avrc_key.key_state = 1;
- m_key_state = 0;
- } else {
- param.avrc_key.key_state = 0;
- m_key_state = 1;
- }
- param.avrc_key.id = 0x41; // volume up
- BT_APP_TRACE_EVENT("-----key_tmr_hdlr: %d, key_id %d---\n", m_key_state, param.avrc_key.id);
- bt_app_transfer_context(bt_app_handle_evt, ESP_AVRC_KEY_STATE_TO, ¶m, sizeof(bt_app_evt_arg), NULL);
- }
-
- static void bt_app_a2d_cb(uint32_t event, void *param)
++// #include "EspAudio.h"
++// #include "EspAudioCom.h"
+
+#include "bt_app_common.h"
+#include "esp_bt_stack_manager.h"
+#include "esp_gap_bt_api.h"
+#include "esp_a2dp_api.h"
+
+#include "bt_rc.h"
+
+typedef enum {
+ BT_APP_EVT_STACK_ON = 0xa0,
+ BT_APP_EVT_MAX
+} bt_app_evt_t;
+
+typedef struct {
+ bool state;
+ bt_bdaddr_t bd_addr;
+} esp_avrc_conn_state_t;
+
+typedef struct {
+ int id;
+ int key_state;
+} esp_avrc_passthrough_rsp_t;
+
+typedef struct {
+ int id;
+ int key_state;
+} esp_avrc_key_state_t;
+
+typedef union {
+ esp_a2d_cb_param_t a2d;
+ esp_avrc_conn_state_t avrc_state;
+ esp_avrc_passthrough_rsp_t avrc_passthrough_rsp;
+ esp_avrc_key_state_t avrc_key;
+} bt_app_evt_arg;
+
+/// AVRC callback events
+typedef enum {
+ ESP_AVRC_CONNECTION_STATE_EVT = 5, /*!< connection state changed event */
+ ESP_AVRC_PASSTHROUGH_RSP_EVT, /*!< AVRC PASSTHROUGH commands */
+ ESP_AVRC_KEY_STATE_TO
+} esp_avrc_cb_event_t;
+
+static esp_a2d_audio_state_t m_audio_state = ESP_A2D_AUDIO_STATE_STOPPED;
+static TimerHandle_t m_key_tmr = 0;
+static int m_key_state = 1; // 0 for pressed, 1 for released
+static xTaskHandle xKeyTaskHandle = 0;
+
+static void bt_app_handle_evt(uint16_t event, void *p_param);
+
+static void key_press_task_handler(void *arg)
+{
+ int key_id = 0x48; // rewind
+ for(;;) {
+ if (m_audio_state != ESP_A2D_AUDIO_STATE_STARTED) {
+ BT_APP_TRACE_EVENT("-----key_tmr_hdlr, return, audio state: %d\n", m_audio_state);
+ vTaskDelay(5000 / portTICK_PERIOD_MS);
+ continue;
+ }
+
+ bt_app_evt_arg param;
+ memset(¶m, 0, sizeof(bt_app_evt_arg));
+ if (m_key_state == 1) {
+ param.avrc_key.key_state = 1;
+ m_key_state = 0;
+ vTaskDelay(5000 / portTICK_PERIOD_MS);
+ } else {
+ param.avrc_key.key_state = 0;
+ m_key_state = 1;
+ vTaskDelay(30 / portTICK_PERIOD_MS);
+ }
+ param.avrc_key.id = key_id; // 0x41 volume up, 0x4b FORWARD
+
+ BT_APP_TRACE_EVENT("-----key_task_hdlr: %d, key_id %d---\n", m_key_state, param.avrc_key.id);
+ bt_app_transfer_context(bt_app_handle_evt, ESP_AVRC_KEY_STATE_TO, ¶m, sizeof(bt_app_evt_arg), NULL);
+ }
+}
+
- EspAudioPlayerStreamWrite((uint8_t *)data, len, 10);
++static void bt_app_a2d_cb(esp_a2d_cb_event_t event, esp_a2d_cb_param_t *param)
+{
+ switch (event) {
+ case ESP_A2D_CONNECTION_STATE_EVT:
+ case ESP_A2D_AUDIO_STATE_EVT:
+ case ESP_A2D_AUDIO_CFG_EVT:
+ {
+ bt_app_transfer_context(bt_app_handle_evt, event, param, sizeof(bt_app_evt_arg), NULL);
+ break;
+ }
+ default:
+ BT_APP_TRACE_ERROR("===a2dp invalid cb event: %d\n", event);
+ break;
+ }
+}
+
+static void btrc_passthrough_rsp_cb(int id, int key_state)
+{
+ bt_app_evt_arg param;
+ memset(¶m, 0, sizeof(bt_app_evt_arg));
+ param.avrc_passthrough_rsp.id = id;
+ param.avrc_passthrough_rsp.key_state = key_state;
+ bt_app_transfer_context(bt_app_handle_evt, ESP_AVRC_PASSTHROUGH_RSP_EVT, ¶m, sizeof(bt_app_evt_arg), NULL);
+}
+
+static void btrc_conn_state_cb(bool state, bt_bdaddr_t *bd_addr)
+{
+ bt_app_evt_arg param;
+ memset(¶m, 0, sizeof(bt_app_evt_arg));
+ param.avrc_state.state = state;
+ memcpy(¶m.avrc_state.bd_addr, bd_addr, sizeof(bt_bdaddr_t));
+ bt_app_transfer_context(bt_app_handle_evt, ESP_AVRC_CONNECTION_STATE_EVT, ¶m, sizeof(bt_app_evt_arg), NULL);
+}
+
+static btrc_ctrl_callbacks_t btrc_ctrl_cb = {
+ sizeof(btrc_ctrl_callbacks_t),
+ btrc_passthrough_rsp_cb,
+ btrc_conn_state_cb
+};
+
+static void bt_app_a2d_data_cb(const uint8_t *data, uint32_t len)
+{
- esp_a2d_register_callback(bt_app_a2d_cb);
++ // EspAudioPlayerStreamWrite((uint8_t *)data, len, 10);
+}
+
+static void bt_app_handle_evt(uint16_t event, void *p_param)
+{
+ BT_APP_TRACE_DEBUG("bt_app_handle_evt 0x%x\n", event);
+ esp_a2d_cb_param_t *a2d = NULL;
+ switch (event) {
+ case BT_APP_EVT_STACK_ON: {
+ char *dev_name = "ESP_SPEAKER";
+ esp_bt_gap_set_device_name(dev_name);
+
- #if 0
- int32_t key_tmr_id = 10;
- m_key_tmr = xTimerCreate("appKeyTmr", 3000 / portTICK_PERIOD_MS, pdTRUE, (void *) key_tmr_id, key_tmr_handler);
- if (xTimerStart(m_key_tmr, 10 / portTICK_PERIOD_MS) != pdTRUE) {
- BT_APP_TRACE_EVENT(" timer start failed\n");
- }
- #endif
++ esp_a2d_register_callback(&bt_app_a2d_cb);
+ esp_a2d_register_data_callback(bt_app_a2d_data_cb);
+
+ esp_a2d_sink_init();
+
+ btrc_ctrl_init(&btrc_ctrl_cb);
+ esp_bt_gap_set_scan_mode(BT_SCAN_MODE_CONNECTABLE_DISCOVERABLE);
+ break;
+ }
+ case ESP_A2D_CONNECTION_STATE_EVT: {
+ a2d = (esp_a2d_cb_param_t *)(p_param);
+ BT_APP_TRACE_EVENT("===a2dp conn_state_cb %d ===\n", a2d->conn_stat.state);
+ break;
+ }
+ case ESP_A2D_AUDIO_STATE_EVT: {
+ a2d = (esp_a2d_cb_param_t *)(p_param);
+ BT_APP_TRACE_EVENT("===a2dp audio_state_cb %d, %d===\n", a2d->audio_stat.state, (int)m_key_tmr);
+ m_audio_state = a2d->audio_stat.state;
+ if (m_audio_state == ESP_A2D_AUDIO_STATE_STARTED &&
+ m_key_tmr == 0) {
+ BT_APP_TRACE_EVENT("mm1\n");
+ xTaskCreate(key_press_task_handler, "keyT", 2048, NULL, 10, &xKeyTaskHandle);
- EspAudioPlayerStreamCfg(StreamSampleRate_44k, 2, StreamBitLen_16BIT);
- EspAudio_SetupStream("stream.pcm", InputSrcType_Stream);
- EspAudio_SetVolume(99);
+ }
+ break;
+ }
+ case ESP_A2D_AUDIO_CFG_EVT: {
+ a2d = (esp_a2d_cb_param_t *)(p_param);
+ BT_APP_TRACE_EVENT("===a2dp audio_cfg_cb type %d ===\n", a2d->audio_cfg.mcc.type);
+ if (a2d->audio_cfg.mcc.type == ESP_A2D_MCT_SBC) {
+ // temporarily hardcoded the PCM configuaration
+ BT_APP_TRACE_EVENT("configure audio player\n");
++ // EspAudioPlayerStreamCfg(StreamSampleRate_44k, 2, StreamBitLen_16BIT);
++ // EspAudio_SetupStream("stream.pcm", InputSrcType_Stream);
++ // EspAudio_SetVolume(99);
+ }
+ break;
+ }
+ case ESP_AVRC_CONNECTION_STATE_EVT: {
+ esp_avrc_conn_state_t *conn_state = (esp_avrc_conn_state_t *)(p_param);
+ BT_APP_TRACE_EVENT("===avrc conn_state evt %d ===\n", conn_state->state);
+ break;
+ }
+ case ESP_AVRC_PASSTHROUGH_RSP_EVT: {
+ esp_avrc_passthrough_rsp_t *passthrough_rsp = (esp_avrc_passthrough_rsp_t *)(p_param);
+ BT_APP_TRACE_EVENT("===avrc passthrough evt id 0x%x, key_state %d===\n", passthrough_rsp->id, passthrough_rsp->key_state);
+ break;
+ }
+ case ESP_AVRC_KEY_STATE_TO: {
+ esp_avrc_key_state_t *key_s = (esp_avrc_key_state_t *)(p_param);
+ BT_APP_TRACE_EVENT("===avrc send key id 0x%x, state %d\n", key_s->id, key_s->key_state);
+ btrc_ctrl_send_passthrough_cmd(NULL, key_s->id, key_s->key_state);
+ break;
+ }
+ default:
+ BT_APP_TRACE_ERROR("===application invalid event: %d\n", event);
+ break;
+ }
+
+}
+
+void app_main_entry(void)
+{
+ esp_err_t init, enable;
+ init = esp_bt_init_stack();
+ if (init != ESP_OK) {
+ return;
+ }
+
+ enable = esp_bt_enable_stack();
+ if (enable != ESP_OK) {
+ return;
+ }
+
+ bt_app_transfer_context(bt_app_handle_evt, BT_APP_EVT_STACK_ON, NULL, 0, NULL);
+}
--- /dev/null
- bt_controller_init();
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include "bt.h"
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+
+#include "nvs_flash.h"
+#include "esp_system.h"
+// #include "EspAudio.h"
+
+extern void bte_main_boot_entry(void *);
+extern void bt_app_task_start_up(void);
+extern void bt_app_core_start(void);
+
+void app_main()
+{
+ nvs_flash_init();
+ // system_init();
+ // printf("Free memory: %d bytes\n", system_get_free_heap_size());
+ // EspAudio_Init();
++ esp_bt_controller_init();
+ bt_app_task_start_up();
+}