component/bt: add AVRC controller example with simple PASSTHROUGH cmd

This commit is contained in:
wangmengyang 2017-01-05 15:43:44 +08:00
parent fa72a98635
commit 44914c17bc
7 changed files with 2536 additions and 123 deletions

View file

@ -95,7 +95,7 @@ typedef struct {
static esp_profile_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;
static TIMER_LIST_ENT tle_av_open_on_rc;
// TODO: need protection against race
#define BTIF_A2D_CB_TO_APP(_event, _param) do { \
@ -125,6 +125,7 @@ else\
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);
@ -146,6 +147,9 @@ 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;
/*****************************************************************************
@ -202,6 +206,34 @@ const char *dump_av_sm_event_name(btif_av_sm_event_t 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
@ -280,15 +312,40 @@ static BOOLEAN btif_av_state_idle_handler(btif_sm_event_t event, void *p_data)
} 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:
case BTA_AV_RC_CLOSE_EVT:
BTIF_TRACE_WARNING("%s : unhandled RC event:%s\n", __FUNCTION__,
dump_av_sm_event_name(event));
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));
@ -354,7 +411,6 @@ static BOOLEAN btif_av_state_opening_handler(btif_sm_event_t event, void *p_data
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_SRC_INCLUDED == TRUE)
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,
@ -365,7 +421,6 @@ static BOOLEAN btif_av_state_opening_handler(btif_sm_event_t event, void *p_data
/* Bring up AVRCP connection too */
BTA_AvOpenRc(btif_av_cb.bta_handle);
}
#endif
btif_queue_advance();
} break;
@ -461,8 +516,7 @@ static BOOLEAN btif_av_state_closing_handler(btif_sm_event_t event, void *p_data
/* Handle the RC_CLOSE event for the cleanup */
case BTA_AV_RC_CLOSE_EVT:
BTIF_TRACE_WARNING("%s : unhandled RC event:%s\n", __FUNCTION__,
dump_av_sm_event_name(event));
btif_rc_handler(event, (tBTA_AV *)p_data);
break;
default:
@ -540,6 +594,9 @@ static BOOLEAN btif_av_state_opened_handler(btif_sm_event_t event, void *p_data)
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));
@ -656,6 +713,9 @@ static BOOLEAN btif_av_state_started_handler(btif_sm_event_t event, void *p_data
/* 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));
@ -1108,7 +1168,9 @@ bt_status_t btif_av_execute_service(BOOLEAN b_enable)
/* 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_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 {

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,309 @@
/*
* Copyright (C) 2012 The Android Open Source Project
*
* 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 __BT_RC_H__
#define __BT_RC_H__
#include <stdint.h>
#include <stdbool.h>
#include "bt_defs.h"
/* Macros */
#define BTRC_MAX_ATTR_STR_LEN 255
#define BTRC_UID_SIZE 8
#define BTRC_MAX_APP_SETTINGS 8
#define BTRC_MAX_FOLDER_DEPTH 4
#define BTRC_MAX_APP_ATTR_SIZE 16
#define BTRC_MAX_ELEM_ATTR_SIZE 7
typedef uint8_t btrc_uid_t[BTRC_UID_SIZE];
typedef enum {
BTRC_FEAT_NONE = 0x00, /* AVRCP 1.0 */
BTRC_FEAT_METADATA = 0x01, /* AVRCP 1.3 */
BTRC_FEAT_ABSOLUTE_VOLUME = 0x02, /* Supports TG role and volume sync */
BTRC_FEAT_BROWSE = 0x04, /* AVRCP 1.4 and up, with Browsing support */
} btrc_remote_features_t;
typedef enum {
BTRC_PLAYSTATE_STOPPED = 0x00, /* Stopped */
BTRC_PLAYSTATE_PLAYING = 0x01, /* Playing */
BTRC_PLAYSTATE_PAUSED = 0x02, /* Paused */
BTRC_PLAYSTATE_FWD_SEEK = 0x03, /* Fwd Seek*/
BTRC_PLAYSTATE_REV_SEEK = 0x04, /* Rev Seek*/
BTRC_PLAYSTATE_ERROR = 0xFF, /* Error */
} btrc_play_status_t;
typedef enum {
BTRC_EVT_PLAY_STATUS_CHANGED = 0x01,
BTRC_EVT_TRACK_CHANGE = 0x02,
BTRC_EVT_TRACK_REACHED_END = 0x03,
BTRC_EVT_TRACK_REACHED_START = 0x04,
BTRC_EVT_PLAY_POS_CHANGED = 0x05,
BTRC_EVT_APP_SETTINGS_CHANGED = 0x08,
} btrc_event_id_t;
typedef enum {
BTRC_NOTIFICATION_TYPE_INTERIM = 0,
BTRC_NOTIFICATION_TYPE_CHANGED = 1,
} btrc_notification_type_t;
typedef enum {
BTRC_PLAYER_ATTR_EQUALIZER = 0x01,
BTRC_PLAYER_ATTR_REPEAT = 0x02,
BTRC_PLAYER_ATTR_SHUFFLE = 0x03,
BTRC_PLAYER_ATTR_SCAN = 0x04,
} btrc_player_attr_t;
typedef enum {
BTRC_MEDIA_ATTR_TITLE = 0x01,
BTRC_MEDIA_ATTR_ARTIST = 0x02,
BTRC_MEDIA_ATTR_ALBUM = 0x03,
BTRC_MEDIA_ATTR_TRACK_NUM = 0x04,
BTRC_MEDIA_ATTR_NUM_TRACKS = 0x05,
BTRC_MEDIA_ATTR_GENRE = 0x06,
BTRC_MEDIA_ATTR_PLAYING_TIME = 0x07,
} btrc_media_attr_t;
typedef enum {
BTRC_PLAYER_VAL_OFF_REPEAT = 0x01,
BTRC_PLAYER_VAL_SINGLE_REPEAT = 0x02,
BTRC_PLAYER_VAL_ALL_REPEAT = 0x03,
BTRC_PLAYER_VAL_GROUP_REPEAT = 0x04
} btrc_player_repeat_val_t;
typedef enum {
BTRC_PLAYER_VAL_OFF_SHUFFLE = 0x01,
BTRC_PLAYER_VAL_ALL_SHUFFLE = 0x02,
BTRC_PLAYER_VAL_GROUP_SHUFFLE = 0x03
} btrc_player_shuffle_val_t;
typedef enum {
BTRC_STS_BAD_CMD = 0x00, /* Invalid command */
BTRC_STS_BAD_PARAM = 0x01, /* Invalid parameter */
BTRC_STS_NOT_FOUND = 0x02, /* Specified parameter is wrong or not found */
BTRC_STS_INTERNAL_ERR = 0x03, /* Internal Error */
BTRC_STS_NO_ERROR = 0x04 /* Operation Success */
} btrc_status_t;
typedef struct {
uint8_t num_attr;
uint8_t attr_ids[BTRC_MAX_APP_SETTINGS];
uint8_t attr_values[BTRC_MAX_APP_SETTINGS];
} btrc_player_settings_t;
typedef union
{
btrc_play_status_t play_status;
btrc_uid_t track; /* queue position in NowPlaying */
uint32_t song_pos;
btrc_player_settings_t player_setting;
} btrc_register_notification_t;
typedef struct {
uint8_t id; /* can be attr_id or value_id */
uint8_t text[BTRC_MAX_ATTR_STR_LEN];
} btrc_player_setting_text_t;
typedef struct {
uint32_t attr_id;
uint8_t text[BTRC_MAX_ATTR_STR_LEN];
} btrc_element_attr_val_t;
/** Callback for the controller's supported feautres */
typedef void (* btrc_remote_features_callback)(bt_bdaddr_t *bd_addr,
btrc_remote_features_t features);
/** Callback for play status request */
typedef void (* btrc_get_play_status_callback)();
/** Callback for list player application attributes (Shuffle, Repeat,...) */
typedef void (* btrc_list_player_app_attr_callback)();
/** Callback for list player application attributes (Shuffle, Repeat,...) */
typedef void (* btrc_list_player_app_values_callback)(btrc_player_attr_t attr_id);
/** Callback for getting the current player application settings value
** num_attr: specifies the number of attribute ids contained in p_attrs
*/
typedef void (* btrc_get_player_app_value_callback) (uint8_t num_attr, btrc_player_attr_t *p_attrs);
/** Callback for getting the player application settings attributes' text
** num_attr: specifies the number of attribute ids contained in p_attrs
*/
typedef void (* btrc_get_player_app_attrs_text_callback) (uint8_t num_attr, btrc_player_attr_t *p_attrs);
/** Callback for getting the player application settings values' text
** num_attr: specifies the number of value ids contained in p_vals
*/
typedef void (* btrc_get_player_app_values_text_callback) (uint8_t attr_id, uint8_t num_val, uint8_t *p_vals);
/** Callback for setting the player application settings values */
typedef void (* btrc_set_player_app_value_callback) (btrc_player_settings_t *p_vals);
/** Callback to fetch the get element attributes of the current song
** num_attr: specifies the number of attributes requested in p_attrs
*/
typedef void (* btrc_get_element_attr_callback) (uint8_t num_attr, btrc_media_attr_t *p_attrs);
/** Callback for register notification (Play state change/track change/...)
** param: Is only valid if event_id is BTRC_EVT_PLAY_POS_CHANGED
*/
typedef void (* btrc_register_notification_callback) (btrc_event_id_t event_id, uint32_t param);
/* AVRCP 1.4 Enhancements */
/** Callback for volume change on CT
** volume: Current volume setting on the CT (0-127)
*/
typedef void (* btrc_volume_change_callback) (uint8_t volume, uint8_t ctype);
/** Callback for passthrough commands */
typedef void (* btrc_passthrough_cmd_callback) (int id, int key_state);
/** BT-RC Target callback structure. */
typedef struct {
/** set to sizeof(BtRcCallbacks) */
size_t size;
btrc_remote_features_callback remote_features_cb;
btrc_get_play_status_callback get_play_status_cb;
btrc_list_player_app_attr_callback list_player_app_attr_cb;
btrc_list_player_app_values_callback list_player_app_values_cb;
btrc_get_player_app_value_callback get_player_app_value_cb;
btrc_get_player_app_attrs_text_callback get_player_app_attrs_text_cb;
btrc_get_player_app_values_text_callback get_player_app_values_text_cb;
btrc_set_player_app_value_callback set_player_app_value_cb;
btrc_get_element_attr_callback get_element_attr_cb;
btrc_register_notification_callback register_notification_cb;
btrc_volume_change_callback volume_change_cb;
btrc_passthrough_cmd_callback passthrough_cmd_cb;
} btrc_callbacks_t;
/** Represents the standard BT-RC AVRCP Target interface. */
typedef struct {
/** set to sizeof(BtRcInterface) */
size_t size;
/**
* Register the BtRc callbacks
*/
bt_status_t (*init)( btrc_callbacks_t* callbacks );
/** Respose to GetPlayStatus request. Contains the current
** 1. Play status
** 2. Song duration/length
** 3. Song position
*/
bt_status_t (*get_play_status_rsp)( btrc_play_status_t play_status, uint32_t song_len, uint32_t song_pos);
/** Lists the support player application attributes (Shuffle/Repeat/...)
** num_attr: Specifies the number of attributes contained in the pointer p_attrs
*/
bt_status_t (*list_player_app_attr_rsp)( int num_attr, btrc_player_attr_t *p_attrs);
/** Lists the support player application attributes (Shuffle Off/On/Group)
** num_val: Specifies the number of values contained in the pointer p_vals
*/
bt_status_t (*list_player_app_value_rsp)( int num_val, uint8_t *p_vals);
/** Returns the current application attribute values for each of the specified attr_id */
bt_status_t (*get_player_app_value_rsp)( btrc_player_settings_t *p_vals);
/** Returns the application attributes text ("Shuffle"/"Repeat"/...)
** num_attr: Specifies the number of attributes' text contained in the pointer p_attrs
*/
bt_status_t (*get_player_app_attr_text_rsp)( int num_attr, btrc_player_setting_text_t *p_attrs);
/** Returns the application attributes text ("Shuffle"/"Repeat"/...)
** num_attr: Specifies the number of attribute values' text contained in the pointer p_vals
*/
bt_status_t (*get_player_app_value_text_rsp)( int num_val, btrc_player_setting_text_t *p_vals);
/** Returns the current songs' element attributes text ("Title"/"Album"/"Artist")
** num_attr: Specifies the number of attributes' text contained in the pointer p_attrs
*/
bt_status_t (*get_element_attr_rsp)( uint8_t num_attr, btrc_element_attr_val_t *p_attrs);
/** Response to set player attribute request ("Shuffle"/"Repeat")
** rsp_status: Status of setting the player attributes for the current media player
*/
bt_status_t (*set_player_app_value_rsp)(btrc_status_t rsp_status);
/* Response to the register notification request (Play state change/track change/...).
** event_id: Refers to the event_id this notification change corresponds too
** type: Response type - interim/changed
** p_params: Based on the event_id, this parameter should be populated
*/
bt_status_t (*register_notification_rsp)(btrc_event_id_t event_id,
btrc_notification_type_t type,
btrc_register_notification_t *p_param);
/* AVRCP 1.4 enhancements */
/**Send current volume setting to remote side. Support limited to SetAbsoluteVolume
** This can be enhanced to support Relative Volume (AVRCP 1.0).
** With RelateVolume, we will send VOLUME_UP/VOLUME_DOWN opposed to absolute volume level
** volume: Should be in the range 0-127. bit7 is reseved and cannot be set
*/
bt_status_t (*set_volume)(uint8_t volume);
/** Closes the interface. */
void (*cleanup)( void );
} btrc_interface_t;
typedef void (* btrc_passthrough_rsp_callback) (int id, int key_state);
typedef void (* btrc_connection_state_callback) (bool state, bt_bdaddr_t *bd_addr);
/** BT-RC Controller callback structure. */
typedef struct {
/** set to sizeof(BtRcCallbacks) */
size_t size;
btrc_passthrough_rsp_callback passthrough_rsp_cb;
btrc_connection_state_callback connection_state_cb;
} btrc_ctrl_callbacks_t;
#if 0
/** Represents the standard BT-RC AVRCP Controller interface. */
typedef struct {
/** set to sizeof(BtRcInterface) */
size_t size;
/**
* Register the BtRc callbacks
*/
bt_status_t (*init)( btrc_ctrl_callbacks_t* callbacks );
/** send pass through command to target */
bt_status_t (*send_pass_through_cmd) ( bt_bdaddr_t *bd_addr, uint8_t key_code, uint8_t key_state );
/** Closes the interface. */
void (*cleanup)( void );
} btrc_ctrl_interface_t;
#endif
/**
* Register the BtRc callbacks
*/
bt_status_t btrc_ctrl_init(btrc_ctrl_callbacks_t *callbacks);
/** send pass through command to target */
bt_status_t btrc_ctrl_send_passthrough_cmd(bt_bdaddr_t *bd_addr, uint8_t key_code, uint8_t key_state);
/** Closes the interface. */
void btrc_ctrl_cleanup(void);
#endif /* __BT_RC_H__ */

View file

@ -532,9 +532,7 @@ extern "C" {
#define BUS_GSC 0x1A
/* User input interface */
#define _IO(a, b) (0) // temporary hack
#define _IOW(a, b, c) (1) // temporary hack
#if UINPUT_INCLUDED
#define UINPUT_IOCTL_BASE 'U'
#define UI_DEV_CREATE _IO(UINPUT_IOCTL_BASE, 1)
@ -551,6 +549,8 @@ extern "C" {
#define UI_SET_PHYS _IOW(UINPUT_IOCTL_BASE, 108, char*)
#define UI_SET_SWBIT _IOW(UINPUT_IOCTL_BASE, 109, int)
#endif /* UINPUT_INCLUDED */
#ifndef NBITS
#define NBITS(x) ((((x) - 1) / (sizeof(long) * 8)) + 1)
#endif

View file

@ -1013,7 +1013,7 @@
/* The maximum number of SDP records the server can support. */
#ifndef SDP_MAX_RECORDS
#define SDP_MAX_RECORDS 4 /*max is 30*/
#define SDP_MAX_RECORDS 6 /*max is 30*/
#endif
/* The maximum number of attributes in each record. */

View file

@ -1,110 +0,0 @@
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
// #include "EspAudio.h"
// #include "EspAudioCom.h"
#include "bt_app_common.h"
#include "esp_bt_stack_manager.h"
#include "esp_gap_bt_api.h"
// #include "bta_api.h"
#include "esp_a2dp_api.h"
typedef enum {
BT_APP_EVT_STACK_ON = 0xa0,
BT_APP_EVT_MAX
} bt_app_evt_t;
typedef union {
esp_a2d_cb_param_t a2d;
} bt_app_evt_arg;
static void bt_app_handle_evt(uint16_t event, void *p_param);
static void bt_app_a2d_cb(uint32_t event, void *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 bt_app_a2d_data_cb(const uint8_t *data, uint32_t len)
{
// 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);
esp_a2d_register_callback(bt_app_a2d_cb);
esp_a2d_register_data_callback(bt_app_a2d_data_cb);
esp_a2d_sink_init();
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 ===\n", a2d->audio_stat.state);
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;
}
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);
}

View file

@ -0,0 +1,246 @@
#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"
#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(&param, 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, &param, sizeof(bt_app_evt_arg), NULL);
}
}
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(&param, 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, &param, sizeof(bt_app_evt_arg), NULL);
}
static void bt_app_a2d_cb(uint32_t event, void *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(&param, 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, &param, 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(&param, 0, sizeof(bt_app_evt_arg));
param.avrc_state.state = state;
memcpy(&param.avrc_state.bd_addr, bd_addr, sizeof(bt_bdaddr_t));
bt_app_transfer_context(bt_app_handle_evt, ESP_AVRC_CONNECTION_STATE_EVT, &param, 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)
{
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);
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);
#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
}
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);
}