Merge branch 'feature/btdm_AVRCP_TG' into 'master'

component/bt: implement AVRCP Target APIs

See merge request idf/esp-idf!3899
This commit is contained in:
Jiang Jiang Jian 2019-04-15 18:53:51 +08:00
commit 603d293365
32 changed files with 2451 additions and 474 deletions

View file

@ -123,6 +123,7 @@ if(CONFIG_BT_ENABLED)
"bluedroid/btc/profile/std/a2dp/btc_a2dp_source.c"
"bluedroid/btc/profile/std/a2dp/btc_av.c"
"bluedroid/btc/profile/std/avrc/btc_avrc.c"
"bluedroid/btc/profile/std/avrc/bta_avrc_co.c"
"bluedroid/btc/profile/std/gap/btc_gap_ble.c"
"bluedroid/btc/profile/std/gap/btc_gap_bt.c"
"bluedroid/btc/profile/std/gatt/btc_gatt_common.c"

View file

@ -32,7 +32,7 @@ esp_err_t esp_avrc_ct_register_callback(esp_avrc_ct_cb_t callback)
return ESP_FAIL;
}
btc_profile_cb_set(BTC_PID_AVRC, callback);
btc_profile_cb_set(BTC_PID_AVRC_CT, callback);
return ESP_OK;
}
@ -45,8 +45,8 @@ esp_err_t esp_avrc_ct_init(void)
btc_msg_t msg;
msg.sig = BTC_SIG_API_CALL;
msg.pid = BTC_PID_AVRC;
msg.act = BTC_AVRC_CTRL_API_INIT_EVT;
msg.pid = BTC_PID_AVRC_CT;
msg.act = BTC_AVRC_CT_API_INIT_EVT;
/* Switch to BTC context */
bt_status_t stat = btc_transfer_context(&msg, NULL, 0, NULL);
@ -62,8 +62,8 @@ esp_err_t esp_avrc_ct_deinit(void)
btc_msg_t msg;
msg.sig = BTC_SIG_API_CALL;
msg.pid = BTC_PID_AVRC;
msg.act = BTC_AVRC_CTRL_API_DEINIT_EVT;
msg.pid = BTC_PID_AVRC_CT;
msg.act = BTC_AVRC_CT_API_DEINIT_EVT;
/* Switch to BTC context */
bt_status_t stat = btc_transfer_context(&msg, NULL, 0, NULL);
@ -82,8 +82,8 @@ esp_err_t esp_avrc_ct_send_set_player_value_cmd(uint8_t tl, uint8_t attr_id, uin
btc_msg_t msg;
msg.sig = BTC_SIG_API_CALL;
msg.pid = BTC_PID_AVRC;
msg.act = BTC_AVRC_CTRL_API_SET_PLAYER_SETTING_EVT;
msg.pid = BTC_PID_AVRC_CT;
msg.act = BTC_AVRC_CTRL_API_SND_SET_PLAYER_SETTING_EVT;
btc_avrc_args_t arg;
memset(&arg, 0, sizeof(btc_avrc_args_t));
@ -97,6 +97,30 @@ esp_err_t esp_avrc_ct_send_set_player_value_cmd(uint8_t tl, uint8_t attr_id, uin
return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL;
}
esp_err_t esp_avrc_ct_send_get_rn_capabilities_cmd(uint8_t tl)
{
if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) {
return ESP_ERR_INVALID_STATE;
}
if (tl >= 16) {
return ESP_ERR_INVALID_ARG;
}
btc_msg_t msg;
msg.sig = BTC_SIG_API_CALL;
msg.pid = BTC_PID_AVRC_CT;
msg.act = BTC_AVRC_STATUS_API_SND_GET_RN_CAPS_EVT;
btc_avrc_args_t arg;
memset(&arg, 0, sizeof(btc_avrc_args_t));
arg.get_caps_cmd.tl = tl;
/* Switch to BTC context */
bt_status_t stat = btc_transfer_context(&msg, &arg, sizeof(btc_avrc_args_t), NULL);
return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL;
}
esp_err_t esp_avrc_ct_send_register_notification_cmd(uint8_t tl, uint8_t event_id, uint32_t event_parameter)
{
@ -108,9 +132,13 @@ esp_err_t esp_avrc_ct_send_register_notification_cmd(uint8_t tl, uint8_t event_i
return ESP_FAIL;
}
if (!btc_avrc_ct_rn_evt_supported(event_id)) {
return ESP_ERR_NOT_SUPPORTED;
}
btc_msg_t msg;
msg.sig = BTC_SIG_API_CALL;
msg.pid = BTC_PID_AVRC;
msg.pid = BTC_PID_AVRC_CT;
msg.act = BTC_AVRC_NOTIFY_API_SND_REG_NOTIFY_EVT;
btc_avrc_args_t arg;
@ -137,7 +165,7 @@ esp_err_t esp_avrc_ct_send_metadata_cmd(uint8_t tl, uint8_t attr_mask)
btc_msg_t msg;
msg.sig = BTC_SIG_API_CALL;
msg.pid = BTC_PID_AVRC;
msg.pid = BTC_PID_AVRC_CT;
msg.act = BTC_AVRC_STATUS_API_SND_META_EVT;
btc_avrc_args_t arg;
@ -163,7 +191,7 @@ esp_err_t esp_avrc_ct_send_passthrough_cmd(uint8_t tl, uint8_t key_code, uint8_t
btc_msg_t msg;
msg.sig = BTC_SIG_API_CALL;
msg.pid = BTC_PID_AVRC;
msg.pid = BTC_PID_AVRC_CT;
msg.act = BTC_AVRC_CTRL_API_SND_PTCMD_EVT;
btc_avrc_args_t arg;
@ -178,4 +206,246 @@ esp_err_t esp_avrc_ct_send_passthrough_cmd(uint8_t tl, uint8_t key_code, uint8_t
return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL;
}
/*********************************************************************************************/
/** following is the API of AVRCP target role **/
/*********************************************************************************************/
esp_err_t esp_avrc_tg_register_callback(esp_avrc_tg_cb_t callback)
{
if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) {
return ESP_ERR_INVALID_STATE;
}
if (callback == NULL) {
return ESP_FAIL;
}
btc_profile_cb_set(BTC_PID_AVRC_TG, callback);
return ESP_OK;
}
esp_err_t esp_avrc_tg_init(void)
{
if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) {
return ESP_ERR_INVALID_STATE;
}
btc_msg_t msg;
msg.sig = BTC_SIG_API_CALL;
msg.pid = BTC_PID_AVRC_TG;
msg.act = BTC_AVRC_TG_API_INIT_EVT;
/* Switch to BTC context */
bt_status_t stat = btc_transfer_context(&msg, NULL, 0, NULL);
return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL;
}
esp_err_t esp_avrc_tg_deinit(void)
{
if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) {
return ESP_ERR_INVALID_STATE;
}
btc_msg_t msg;
msg.sig = BTC_SIG_API_CALL;
msg.pid = BTC_PID_AVRC_TG;
msg.act = BTC_AVRC_TG_API_DEINIT_EVT;
/* Switch to BTC context */
bt_status_t stat = btc_transfer_context(&msg, NULL, 0, NULL);
return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL;
}
bool esp_avrc_psth_bit_mask_operation(esp_avrc_bit_mask_op_t op, esp_avrc_psth_bit_mask_t *psth,
esp_avrc_pt_cmd_t cmd)
{
if (!psth ||
cmd > ESP_AVRC_PT_CMD_VENDOR) {
return false;
}
uint16_t *p = &psth->bits[(uint8_t)cmd >> 4];
uint16_t mask = (uint16_t)1 << ((uint8_t)cmd & 0x0F);
switch (op) {
case ESP_AVRC_BIT_MASK_OP_SET:
*p |= mask;
break;
case ESP_AVRC_BIT_MASK_OP_CLEAR:
*p &= ~mask;
break;
case ESP_AVRC_BIT_MASK_OP_TEST:
return (*p & mask);
default:
return false;
}
return true;
}
esp_err_t esp_avrc_tg_get_psth_cmd_filter(esp_avrc_psth_filter_t filter, esp_avrc_psth_bit_mask_t *cmd_set)
{
if ((esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) ||
(! btc_avrc_tg_init_p())) {
return ESP_ERR_INVALID_STATE;
}
if (filter >= ESP_AVRC_PSTH_FILTER_SUPPORT_MAX ||
cmd_set == NULL) {
return ESP_ERR_INVALID_ARG;
}
if (filter == ESP_AVRC_PSTH_FILTER_ALLOWED_CMD) {
const uint16_t *allowed_cmd_set = btc_avrc_tg_get_allowed_command();
memcpy(cmd_set, allowed_cmd_set, sizeof(esp_avrc_psth_bit_mask_t));
} else if (filter == ESP_AVRC_PSTH_FILTER_SUPPORTED_CMD) {
const uint16_t *supported_cmd_set = btc_avrc_tg_get_supported_command();
memcpy(cmd_set, supported_cmd_set, sizeof(esp_avrc_psth_bit_mask_t));
} else {
}
return ESP_OK;
}
esp_err_t esp_avrc_tg_set_psth_cmd_filter(esp_avrc_psth_filter_t filter, const esp_avrc_psth_bit_mask_t *cmd_set)
{
if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) {
return ESP_ERR_INVALID_STATE;
}
if (filter >= ESP_AVRC_PSTH_FILTER_SUPPORT_MAX ||
cmd_set == NULL) {
return ESP_ERR_INVALID_ARG;
}
if (filter == ESP_AVRC_PSTH_FILTER_ALLOWED_CMD) {
return ESP_ERR_NOT_SUPPORTED;
}
if (filter == ESP_AVRC_PSTH_FILTER_SUPPORTED_CMD) {
bool allowed = btc_avrc_tg_check_supported_command(cmd_set->bits);
if (!allowed) {
return ESP_ERR_NOT_SUPPORTED;
}
btc_msg_t msg;
msg.sig = BTC_SIG_API_CALL;
msg.pid = BTC_PID_AVRC_TG;
msg.act = BTC_AVRC_TG_API_SET_PSTH_SUPPORTED_CMD_EVT;
btc_avrc_tg_args_t arg;
memset(&arg, 0, sizeof(btc_avrc_tg_args_t));
arg.set_psth_cmd = (uint16_t *)cmd_set->bits;
/* Switch to BTC context */
bt_status_t stat = btc_transfer_context(&msg, &arg, sizeof(btc_avrc_tg_args_t),
btc_avrc_tg_arg_deep_copy);
return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL;
} else {
return ESP_FAIL;
}
}
esp_err_t esp_avrc_tg_get_rn_evt_cap(esp_avrc_rn_evt_cap_t cap, esp_avrc_rn_evt_cap_mask_t *evt_set)
{
if ((esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) ||
(! btc_avrc_tg_init_p())) {
return ESP_ERR_INVALID_STATE;
}
if (cap >= ESP_AVRC_RN_CAP_MAX ||
evt_set == NULL) {
return ESP_ERR_INVALID_ARG;
}
if (cap == ESP_AVRC_RN_CAP_ALLOWED_EVT) {
evt_set->bits = btc_avrc_tg_get_rn_allowed_evt();
} else if (cap == ESP_AVRC_RN_CAP_SUPPORTED_EVT) {
evt_set->bits = btc_avrc_tg_get_rn_supported_evt();
} else {
}
return ESP_OK;
}
esp_err_t esp_avrc_tg_set_rn_evt_cap(const esp_avrc_rn_evt_cap_mask_t *evt_set)
{
if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) {
return ESP_ERR_INVALID_STATE;
}
if (evt_set == NULL) {
return ESP_ERR_INVALID_ARG;
}
bool allowed = btc_avrc_tg_check_rn_supported_evt(evt_set->bits);
if (!allowed) {
return ESP_ERR_NOT_SUPPORTED;
}
btc_msg_t msg;
msg.sig = BTC_SIG_API_CALL;
msg.pid = BTC_PID_AVRC_TG;
msg.act = BTC_AVRC_TG_API_SET_RN_SUPPORTED_EVT;
btc_avrc_tg_args_t arg;
memset(&arg, 0, sizeof(btc_avrc_tg_args_t));
arg.set_rn_evt = evt_set->bits;
/* Switch to BTC context */
bt_status_t stat = btc_transfer_context(&msg, &arg, sizeof(btc_avrc_tg_args_t), NULL);
return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL;
}
bool esp_avrc_rn_evt_bit_mask_operation(esp_avrc_bit_mask_op_t op, esp_avrc_rn_evt_cap_mask_t *events,
esp_avrc_rn_event_ids_t event_id)
{
if (!events ||
event_id >= ESP_AVRC_RN_MAX_EVT) {
return false;
}
uint16_t *p = &events->bits;
uint16_t mask = (uint16_t)1 << ((uint8_t)event_id & 0x0F);
switch (op) {
case ESP_AVRC_BIT_MASK_OP_SET:
*p |= mask;
break;
case ESP_AVRC_BIT_MASK_OP_CLEAR:
*p &= ~mask;
break;
case ESP_AVRC_BIT_MASK_OP_TEST:
return (*p & mask);
default:
return false;
}
return true;
}
esp_err_t esp_avrc_tg_send_rn_rsp(esp_avrc_rn_event_ids_t event_id, esp_avrc_rn_rsp_t rsp,
esp_avrc_rn_param_t *param)
{
if ((esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) ||
(! btc_avrc_tg_connected_p())) {
return ESP_ERR_INVALID_STATE;
}
if ( ! btc_avrc_tg_rn_evt_supported((uint8_t)event_id)) {
return ESP_ERR_NOT_SUPPORTED;
}
btc_msg_t msg;
msg.sig = BTC_SIG_API_CALL;
msg.pid = BTC_PID_AVRC_TG;
msg.act = BTC_AVRC_TG_API_SEND_RN_RSP_EVT;
btc_avrc_tg_args_t arg;
memset(&arg, 0, sizeof(btc_avrc_tg_args_t));
arg.rn_rsp.event_id = event_id;
arg.rn_rsp.rsp = rsp;
memcpy(&arg.rn_rsp.param, param, sizeof(esp_avrc_rn_param_t));
/* Switch to BTC context */
bt_status_t stat = btc_transfer_context(&msg, &arg, sizeof(btc_avrc_tg_args_t), NULL);
return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL;
}
#endif /* #if BTC_AV_INCLUDED */

View file

@ -34,17 +34,97 @@ typedef enum {
ESP_AVRC_FEAT_ADV_CTRL = 0x0200, /*!< remote control advanced control commmand/response */
} esp_avrc_features_t;
/// AVRC supported features flag retrieved in SDP record
typedef enum {
ESP_AVRC_FEAT_FLAG_CAT1 = 0x0001, /*!< category 1 */
ESP_AVRC_FEAT_FLAG_CAT2 = 0x0002, /*!< category 2 */
ESP_AVRC_FEAT_FLAG_CAT3 = 0x0004, /*!< category 3 */
ESP_AVRC_FEAT_FLAG_CAT4 = 0x0008, /*!< category 4 */
ESP_AVRC_FEAT_FLAG_BROWSING = 0x0040, /*!< browsing */
ESP_AVRC_FEAT_FLAG_COVER_ART_GET_IMAGE_PROP = 0x0080, /*!< Cover Art GetImageProperties */
ESP_AVRC_FEAT_FLAG_COVER_ART_GET_IMAGE = 0x0100, /*!< Cover Art GetImage */
ESP_AVRC_FEAT_FLAG_COVER_ART_GET_LINKED_THUMBNAIL = 0x0200, /*!< Cover Art GetLinkedThumbnail */
} esp_avrc_feature_flag_t;
/// AVRC passthrough command code
typedef enum {
ESP_AVRC_PT_CMD_PLAY = 0x44, /*!< play */
ESP_AVRC_PT_CMD_STOP = 0x45, /*!< stop */
ESP_AVRC_PT_CMD_PAUSE = 0x46, /*!< pause */
ESP_AVRC_PT_CMD_FORWARD = 0x4B, /*!< forward */
ESP_AVRC_PT_CMD_BACKWARD = 0x4C, /*!< backward */
ESP_AVRC_PT_CMD_REWIND = 0x48, /*!< rewind */
ESP_AVRC_PT_CMD_FAST_FORWARD = 0x49 /*!< fast forward */
ESP_AVRC_PT_CMD_SELECT = 0x00, /*!< select */
ESP_AVRC_PT_CMD_UP = 0x01, /*!< up */
ESP_AVRC_PT_CMD_DOWN = 0x02, /*!< down */
ESP_AVRC_PT_CMD_LEFT = 0x03, /*!< left */
ESP_AVRC_PT_CMD_RIGHT = 0x04, /*!< right */
ESP_AVRC_PT_CMD_RIGHT_UP = 0x05, /*!< right-up */
ESP_AVRC_PT_CMD_RIGHT_DOWN = 0x06, /*!< right-down */
ESP_AVRC_PT_CMD_LEFT_UP = 0x07, /*!< left-up */
ESP_AVRC_PT_CMD_LEFT_DOWN = 0x08, /*!< left-down */
ESP_AVRC_PT_CMD_ROOT_MENU = 0x09, /*!< root menu */
ESP_AVRC_PT_CMD_SETUP_MENU = 0x0A, /*!< setup menu */
ESP_AVRC_PT_CMD_CONT_MENU = 0x0B, /*!< contents menu */
ESP_AVRC_PT_CMD_FAV_MENU = 0x0C, /*!< favorite menu */
ESP_AVRC_PT_CMD_EXIT = 0x0D, /*!< exit */
ESP_AVRC_PT_CMD_0 = 0x20, /*!< 0 */
ESP_AVRC_PT_CMD_1 = 0x21, /*!< 1 */
ESP_AVRC_PT_CMD_2 = 0x22, /*!< 2 */
ESP_AVRC_PT_CMD_3 = 0x23, /*!< 3 */
ESP_AVRC_PT_CMD_4 = 0x24, /*!< 4 */
ESP_AVRC_PT_CMD_5 = 0x25, /*!< 5 */
ESP_AVRC_PT_CMD_6 = 0x26, /*!< 6 */
ESP_AVRC_PT_CMD_7 = 0x27, /*!< 7 */
ESP_AVRC_PT_CMD_8 = 0x28, /*!< 8 */
ESP_AVRC_PT_CMD_9 = 0x29, /*!< 9 */
ESP_AVRC_PT_CMD_DOT = 0x2A, /*!< dot */
ESP_AVRC_PT_CMD_ENTER = 0x2B, /*!< enter */
ESP_AVRC_PT_CMD_CLEAR = 0x2C, /*!< clear */
ESP_AVRC_PT_CMD_CHAN_UP = 0x30, /*!< channel up */
ESP_AVRC_PT_CMD_CHAN_DOWN = 0x31, /*!< channel down */
ESP_AVRC_PT_CMD_PREV_CHAN = 0x32, /*!< previous channel */
ESP_AVRC_PT_CMD_SOUND_SEL = 0x33, /*!< sound select */
ESP_AVRC_PT_CMD_INPUT_SEL = 0x34, /*!< input select */
ESP_AVRC_PT_CMD_DISP_INFO = 0x35, /*!< display information */
ESP_AVRC_PT_CMD_HELP = 0x36, /*!< help */
ESP_AVRC_PT_CMD_PAGE_UP = 0x37, /*!< page up */
ESP_AVRC_PT_CMD_PAGE_DOWN = 0x38, /*!< page down */
ESP_AVRC_PT_CMD_POWER = 0x40, /*!< power */
ESP_AVRC_PT_CMD_VOL_UP = 0x41, /*!< volume up */
ESP_AVRC_PT_CMD_VOL_DOWN = 0x42, /*!< volume down */
ESP_AVRC_PT_CMD_MUTE = 0x43, /*!< mute */
ESP_AVRC_PT_CMD_PLAY = 0x44, /*!< play */
ESP_AVRC_PT_CMD_STOP = 0x45, /*!< stop */
ESP_AVRC_PT_CMD_PAUSE = 0x46, /*!< pause */
ESP_AVRC_PT_CMD_RECORD = 0x47, /*!< record */
ESP_AVRC_PT_CMD_REWIND = 0x48, /*!< rewind */
ESP_AVRC_PT_CMD_FAST_FORWARD = 0x49, /*!< fast forward */
ESP_AVRC_PT_CMD_EJECT = 0x4A, /*!< eject */
ESP_AVRC_PT_CMD_FORWARD = 0x4B, /*!< forward */
ESP_AVRC_PT_CMD_BACKWARD = 0x4C, /*!< backward */
ESP_AVRC_PT_CMD_ANGLE = 0x50, /*!< angle */
ESP_AVRC_PT_CMD_SUBPICT = 0x51, /*!< subpicture */
ESP_AVRC_PT_CMD_F1 = 0x71, /*!< F1 */
ESP_AVRC_PT_CMD_F2 = 0x72, /*!< F2 */
ESP_AVRC_PT_CMD_F3 = 0x73, /*!< F3 */
ESP_AVRC_PT_CMD_F4 = 0x74, /*!< F4 */
ESP_AVRC_PT_CMD_F5 = 0x75, /*!< F5 */
ESP_AVRC_PT_CMD_VENDOR = 0x7E, /*!< vendor unique */
} esp_avrc_pt_cmd_t;
/// AVRC passthrough command filter
typedef enum {
ESP_AVRC_PSTH_FILTER_ALLOWED_CMD = 0, /*!< all of the PASSTHROUGH commands that can possibly be used, immuateble */
ESP_AVRC_PSTH_FILTER_SUPPORTED_CMD = 1, /*!< PASSTHROUGH commands selectively supported according to the current configuration */
ESP_AVRC_PSTH_FILTER_SUPPORT_MAX,
} esp_avrc_psth_filter_t;
/// AVRC passthrough command bit mask
typedef struct {
uint16_t bits[8]; /*!< bit mask representation of PASSTHROUGH commands */
} esp_avrc_psth_bit_mask_t;
typedef enum {
ESP_AVRC_BIT_MASK_OP_TEST = 0, /*!< operation code to test a specific bit */
ESP_AVRC_BIT_MASK_OP_SET = 1, /*!< operation code to set a specific bit */
ESP_AVRC_BIT_MASK_OP_CLEAR = 2, /*!< operation code to clear a specific bit */
} esp_avrc_bit_mask_op_t;
/// AVRC passthrough command state
typedef enum {
ESP_AVRC_PT_CMD_STATE_PRESSED = 0, /*!< key pressed */
@ -59,8 +139,18 @@ typedef enum {
ESP_AVRC_CT_PLAY_STATUS_RSP_EVT = 3, /*!< play status response event */
ESP_AVRC_CT_CHANGE_NOTIFY_EVT = 4, /*!< notification event */
ESP_AVRC_CT_REMOTE_FEATURES_EVT = 5, /*!< feature of remote device indication event */
ESP_AVRC_CT_GET_RN_CAPABILITIES_RSP_EVT = 6, /*!< supported notification events capability of peer device */
} esp_avrc_ct_cb_event_t;
/// AVRC Target callback events
typedef enum {
ESP_AVRC_TG_CONNECTION_STATE_EVT = 0, /*!< connection state changed event */
ESP_AVRC_TG_REMOTE_FEATURES_EVT = 1, /*!< feature of remote device indication event */
ESP_AVRC_TG_PASSTHROUGH_CMD_EVT = 2, /*!< passthrough command event */
ESP_AVRC_TG_SET_ABSOLUTE_VOLUME_CMD_EVT = 3, /*!< set absolute volume command from remote device */
ESP_AVRC_TG_REGISTER_NOTIFICATION_EVT = 4, /*!< register notification event */
} esp_avrc_tg_cb_event_t;
/// AVRC metadata attribute mask
typedef enum {
ESP_AVRC_MD_ATTR_TITLE = 0x1, /*!< title of the playing track */
@ -82,9 +172,32 @@ typedef enum {
ESP_AVRC_RN_BATTERY_STATUS_CHANGE = 0x06, /*!< battery status changed */
ESP_AVRC_RN_SYSTEM_STATUS_CHANGE = 0x07, /*!< system status changed */
ESP_AVRC_RN_APP_SETTING_CHANGE = 0x08, /*!< application settings changed */
ESP_AVRC_RN_NOW_PLAYING_CHANGE = 0x09, /*!< now playing content changed */
ESP_AVRC_RN_AVAILABLE_PLAYERS_CHANGE = 0x0a, /*!< available players changed */
ESP_AVRC_RN_ADDRESSED_PLAYER_CHANGE = 0x0b, /*!< the addressed player changed */
ESP_AVRC_RN_UIDS_CHANGE = 0x0c, /*!< UIDs changed */
ESP_AVRC_RN_VOLUME_CHANGE = 0x0d, /*!< volume changed locally on TG */
ESP_AVRC_RN_MAX_EVT
} esp_avrc_rn_event_ids_t;
/// AVRC target notification event notification capability
typedef enum {
ESP_AVRC_RN_CAP_ALLOWED_EVT = 0, /*!< all of the notification events that can possibly be supported, immutable */
ESP_AVRC_RN_CAP_SUPPORTED_EVT = 1, /*!< notification events selectively supported according to the current configuration */
ESP_AVRC_RN_CAP_MAX,
} esp_avrc_rn_evt_cap_t;
/// AVRC target notification event capability bit mask
typedef struct {
uint16_t bits; /*!< bit mask representation of PASSTHROUGH commands */
} esp_avrc_rn_evt_cap_mask_t;
/// AVRC notification response type
typedef enum {
ESP_AVRC_RN_RSP_INTERIM = 13, /*!< initial response to RegisterNotification, should be sent T_mtp(1000ms) from receiving the command */
ESP_AVRC_RN_RSP_CHANGED = 15, /*!< final response to RegisterNotification command */
} esp_avrc_rn_rsp_t;
/// AVRC player setting ids
typedef enum {
ESP_AVRC_PS_EQUALIZER = 0x01, /*!< equalizer, on or off */
@ -122,6 +235,46 @@ typedef enum {
ESP_AVRC_PS_SCAN_GROUP = 0x3 /*!< group scan */
} esp_avrc_ps_scn_value_ids_t;
/// AVCTP response codes
typedef enum {
ESP_AVRC_RSP_NOT_IMPL = 8, /*!< not implemented */
ESP_AVRC_RSP_ACCEPT = 9, /*!< accept */
ESP_AVRC_RSP_REJECT = 10, /*!< reject */
ESP_AVRC_RSP_IN_TRANS = 11, /*!< in transition */
ESP_AVRC_RSP_IMPL_STBL = 12, /*!< implemented/stable */
ESP_AVRC_RSP_CHANGED = 13, /*!< changed */
ESP_AVRC_RSP_INTERIM = 15, /*!< interim */
} esp_avrc_rsp_t;
/// AVRCP battery status
typedef enum {
ESP_AVRC_BATT_NORMAL = 0, /*!< normal state */
ESP_AVRC_BATT_WARNING = 1, /*!< unable to operate soon */
ESP_AVRC_BATT_CRITICAL = 2, /*!< cannot operate any more */
ESP_AVRC_BATT_EXTERNAL = 3, /*!< plugged to external power supply */
ESP_AVRC_BATT_FULL_CHARGE = 4, /*!< when completely charged from external power supply */
} esp_avrc_batt_stat_t;
/// AVRCP current status of playback
typedef enum {
ESP_AVRC_PLAYBACK_STOPPED = 0, /*!< stopped */
ESP_AVRC_PLAYBACK_PLAYING = 1, /*!< playing */
ESP_AVRC_PLAYBACK_PAUSED = 2, /*!< paused */
ESP_AVRC_PLAYBACK_FWD_SEEK = 3, /*!< forward seek */
ESP_AVRC_PLAYBACK_REV_SEEK = 4, /*!< reverse seek */
ESP_AVRC_PLAYBACK_ERROR = 0xFF, /*!< error */
} esp_avrc_playback_stat_t;
/// AVRCP notification parameters
typedef union
{
uint8_t volume; /*!< response data for ESP_AVRC_RN_VOLUME_CHANGE, ranges 0..127 */
esp_avrc_playback_stat_t playback; /*!< response data for ESP_AVRC_RN_PLAY_STATUS_CHANGE */
uint8_t elm_id[8]; /*!< response data for ESP_AVRC_RN_TRACK_CHANGE */
uint32_t play_pos; /*!< response data for ESP_AVRC_RN_PLAY_POS_CHANGED, in millisecond */
esp_avrc_batt_stat_t batt; /*!< response data for ESP_AVRC_RN_BATTERY_STATUS_CHANGE */
} esp_avrc_rn_param_t;
/// AVRC controller callback parameters
typedef union {
/**
@ -155,7 +308,7 @@ typedef union {
*/
struct avrc_ct_change_notify_param {
uint8_t event_id; /*!< id of AVRC event notification */
uint32_t event_parameter; /*!< event notification parameter */
esp_avrc_rn_param_t event_parameter; /*!< event notification parameter */
} change_ntf; /*!< notifications */
/**
@ -163,11 +316,61 @@ typedef union {
*/
struct avrc_ct_rmt_feats_param {
uint32_t feat_mask; /*!< AVRC feature mask of remote device */
uint16_t tg_feat_flag; /*!< feature flag of remote device as TG */
esp_bd_addr_t remote_bda; /*!< remote bluetooth device address */
} rmt_feats; /*!< AVRC features discovered from remote SDP server */
/**
* @brief ESP_AVRC_CT_GET_RN_CAPABILITIES_RSP_EVT
*/
struct avrc_ct_get_rn_caps_rsp_param {
uint8_t cap_count; /*!< number of items provided in event or company_id according to cap_id used */
esp_avrc_rn_evt_cap_mask_t evt_set; /*!< supported event_ids represented in bit-mask */
} get_rn_caps_rsp; /*!< get supported event capabilities response from AVRCP target */
} esp_avrc_ct_cb_param_t;
/// AVRC target callback parameters
typedef union {
/**
* @brief ESP_AVRC_TG_CONNECTION_STATE_EVT
*/
struct avrc_tg_conn_stat_param {
bool connected; /*!< whether AVRC connection is set up */
esp_bd_addr_t remote_bda; /*!< remote bluetooth device address */
} conn_stat; /*!< AVRC connection status */
/**
* @brief ESP_AVRC_TG_REMOTE_FEATURES_EVT
*/
struct avrc_tg_rmt_feats_param {
uint32_t feat_mask; /*!< AVRC feature mask of remote device */
uint16_t ct_feat_flag; /*!< feature flag of remote device as CT */
esp_bd_addr_t remote_bda; /*!< remote bluetooth device address */
} rmt_feats; /*!< AVRC features discovered through SDP */
/**
* @brief ESP_AVRC_TG_PASSTHROUGH_CMD_EVT
*/
struct avrc_tg_psth_cmd_param {
uint8_t key_code; /*!< passthrough command code */
uint8_t key_state; /*!< 0 for PRESSED, 1 for RELEASED */
} psth_cmd; /*!< passthrough command */
/**
* @brief ESP_AVRC_TG_SET_ABSOLUTE_VOLUME_CMD_EVT
*/
struct avrc_tg_set_abs_vol_param {
uint8_t volume; /*!< volume ranges from 0 to 127 */
} set_abs_vol; /*!< set absolute volume command targeted on audio sink */
/**
* @brief ESP_AVRC_TG_REGISTER_NOTIFICATION_EVT
*/
struct avrc_tg_reg_ntf_param {
uint8_t event_id; /*!< event id of AVRC RegisterNotification */
uint32_t event_parameter; /*!< event notification parameter */
} reg_ntf; /*!< register notification */
} esp_avrc_tg_cb_param_t;
/**
* @brief AVRCP controller callback function type
@ -176,23 +379,28 @@ typedef union {
*/
typedef void (* esp_avrc_ct_cb_t)(esp_avrc_ct_cb_event_t event, esp_avrc_ct_cb_param_t *param);
/**
* @brief AVRCP target callback function type
* @param event : Event type
* @param param : Pointer to callback parameter union
*/
typedef void (* esp_avrc_tg_cb_t)(esp_avrc_tg_cb_event_t event, esp_avrc_tg_cb_param_t *param);
/**
* @brief Register application callbacks to AVRCP module; for now only AVRCP Controller
* role is supported. This function should be called after esp_bluedroid_enable()
* completes successfully
* @brief Register application callbacks to AVRCP module. This function should be
* called after esp_bluedroid_enable() completes successfully
*
* @param[in] callback: AVRCP controller callback function
*
* @return
* - ESP_OK: success
* - ESP_INVALID_STATE: if bluetooth stack is not yet enabled
* - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled
* - ESP_FAIL: others
*
*/
esp_err_t esp_avrc_ct_register_callback(esp_avrc_ct_cb_t callback);
/**
*
* @brief Initialize the bluetooth AVRCP controller module, This function should be called
@ -200,13 +408,12 @@ esp_err_t esp_avrc_ct_register_callback(esp_avrc_ct_cb_t callback);
*
* @return
* - ESP_OK: success
* - ESP_INVALID_STATE: if bluetooth stack is not yet enabled
* - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled
* - ESP_FAIL: others
*
*/
esp_err_t esp_avrc_ct_init(void);
/**
*
* @brief De-initialize AVRCP controller module. This function should be called after
@ -214,7 +421,7 @@ esp_err_t esp_avrc_ct_init(void);
*
* @return
* - ESP_OK: success
* - ESP_INVALID_STATE: if bluetooth stack is not yet enabled
* - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled
* - ESP_FAIL: others
*/
esp_err_t esp_avrc_ct_deinit(void);
@ -229,11 +436,24 @@ esp_err_t esp_avrc_ct_deinit(void);
* @param[in] value_id : attribute value defined for the specific player application setting attribute
* @return
* - ESP_OK: success
* - ESP_INVALID_STATE: if bluetooth stack is not yet enabled
* - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled
* - ESP_FAIL: others
*/
esp_err_t esp_avrc_ct_send_set_player_value_cmd(uint8_t tl, uint8_t attr_id, uint8_t value_id);
/**
* @brief Send GetCapabilities PDU to AVRCP target to retrieve remote device's supported
* notification event_ids. This function should be called after
* ESP_AVRC_CT_CONNECTION_STATE_EVT is received and AVRCP connection is established
*
* @param[in] tl : transaction label, 0 to 15, consecutive commands should use different values.
* @return
* - ESP_OK: success
* - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled
* - ESP_FAIL: others
*/
esp_err_t esp_avrc_ct_send_get_rn_capabilities_cmd(uint8_t tl);
/**
* @brief Send register notification command to AVRCP target, This function should be called after
* ESP_AVRC_CT_CONNECTION_STATE_EVT is received and AVRCP connection is established
@ -243,7 +463,8 @@ esp_err_t esp_avrc_ct_send_set_player_value_cmd(uint8_t tl, uint8_t attr_id, uin
* @param[in] event_parameter : special parameters, eg. playback interval for ESP_AVRC_RN_PLAY_POS_CHANGED
* @return
* - ESP_OK: success
* - ESP_INVALID_STATE: if bluetooth stack is not yet enabled
* - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled
* - ESP_ERR_NOT_SUPPORTED: if the event_id is not supported in current implementation
* - ESP_FAIL: others
*/
esp_err_t esp_avrc_ct_send_register_notification_cmd(uint8_t tl, uint8_t event_id, uint32_t event_parameter);
@ -258,7 +479,7 @@ esp_err_t esp_avrc_ct_send_register_notification_cmd(uint8_t tl, uint8_t event_i
*
* @return
* - ESP_OK: success
* - ESP_INVALID_STATE: if bluetooth stack is not yet enabled
* - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled
* - ESP_FAIL: others
*/
esp_err_t esp_avrc_ct_send_metadata_cmd(uint8_t tl, uint8_t attr_mask);
@ -275,12 +496,176 @@ esp_err_t esp_avrc_ct_send_metadata_cmd(uint8_t tl, uint8_t attr_mask);
*
* @return
* - ESP_OK: success
* - ESP_INVALID_STATE: if bluetooth stack is not yet enabled
* - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled
* - ESP_FAIL: others
*/
esp_err_t esp_avrc_ct_send_passthrough_cmd(uint8_t tl, uint8_t key_code, uint8_t key_state);
/**
* @brief Register application callbacks to AVRCP target module; This function should be
* called after esp_bluedroid_enable() completes successfully
*
* @param[in] callback: AVRCP target callback function
*
* @return
* - ESP_OK: success
* - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled
* - ESP_FAIL: others
*
*/
esp_err_t esp_avrc_tg_register_callback(esp_avrc_tg_cb_t callback);
/**
*
* @brief Initialize the bluetooth AVRCP target module, This function should be called
* after esp_bluedroid_enable() completes successfully
*
* @return
* - ESP_OK: success
* - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled
* - ESP_FAIL: others
*
*/
esp_err_t esp_avrc_tg_init(void);
/**
*
* @brief De-initialize AVRCP target module. This function should be called after
* after esp_bluedroid_enable() completes successfully
*
* @return
* - ESP_OK: success
* - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled
* - ESP_FAIL: others
*/
esp_err_t esp_avrc_tg_deinit(void);
/**
*
* @brief Get the current filter of remote passthrough commands on AVRC target. Filter is given by
* filter type and bit mask for the passthrough commands. This function should be called
* after esp_avrc_tg_init().
* For filter type ESP_AVRC_PSTH_FILTER_ALLOWED_CMD, the retrieved command set is constant and
* it covers all of the passthrough commands that can possibly be supported.
* For filter type ESP_AVRC_PSTH_FILTER_SUPPORT_COMMANDS, the retrieved command set covers the
* passthrough commands selected to be supported according to current configuration. The
* configuration can be changed using esp_avrc_tg_set_psth_cmd_filter()
*
* @return
* - ESP_OK: success
* - ESP_ERR_INVALID_STATE: if bluetooth stack is not enabled or AVRC TG is not initialized
* - ESP_ERR_INVALID_ARG: if filter type is invalid or cmd_set is NULL
* - ESP_FAIL: otherwise
*/
esp_err_t esp_avrc_tg_get_psth_cmd_filter(esp_avrc_psth_filter_t filter, esp_avrc_psth_bit_mask_t *cmd_set);
/**
*
* @brief Set the filter of remote passthrough commands on AVRC target. Filter is given by
* filter type and bit mask for the passthrough commands. This function should be called
* after esp_avrc_tg_init().
* If filter type is ESP_AVRC_PSTH_FILTER_SUPPORT_CMD, the passthrough commands which
* are set "1" as given in cmd_set will generate ESP_AVRC_CT_PASSTHROUGH_RSP_EVT callback
* event and are auto-accepted in the protocol stack, other commands are replied with response
* type "NOT IMPLEMENTED" (8). The set of supported commands should be a subset of allowed
* command set. The allowed command set can be retrieved using esp_avrc_tg_get_psth_cmd_filter()
* with filter type "ESP_AVRC_PSTH_FILTER_ALLOWED_CMD".
*
* Filter type "ESP_AVRC_PSTH_FILTER_ALLOWED_CMD" does not apply to this function
* @return
* - ESP_OK: success
* - ESP_ERR_INVALID_STATE: if bluetooth stack is not enabled
* - ESP_ERR_INVALID_ARG: if filter type is invalid or cmd_set is NULL
* - ESP_ERR_NOT_SUPPORTED:: if filter type is ESP_AVRC_PSTH_FILTER_ALLOWED_CMD, or cmd_set
* includes unallowed commands
*
*/
esp_err_t esp_avrc_tg_set_psth_cmd_filter(esp_avrc_psth_filter_t filter, const esp_avrc_psth_bit_mask_t *cmd_set);
/**
* @brief Operate on the type esp_avrc_psth_bit_mask_t with regard to a specific PASSTHROUGH command
* @param[in] op: operation requested on the bit mask field
* @param[in] psth: pointer to passthrough command bit mask structure
* @param[in] cmd: passthrough command code
*
* @return For operation ESP_AVRC_BIT_MASK_OP_SET or ESP_AVRC_BIT_MASK_OP_CLEAR, return
* true for a successful operation, otherwise return false
* For operation ESP_AVRC_BIT_MASK_OP_TEST, return true if the corresponding bit
* is set, otherwise false
*
*/
bool esp_avrc_psth_bit_mask_operation(esp_avrc_bit_mask_op_t op, esp_avrc_psth_bit_mask_t *psth,
esp_avrc_pt_cmd_t cmd);
/**
*
* @brief Get the requested event notification capabilies on local AVRC target. The capability is returned
* in a bit mask representation in evt_set. This function should be called after
* esp_avrc_tg_init().
* For capability type "ESP_AVRC_RN_CAP_ALLOWED_EVT, the retrieved event set is constant and
* it covers all of the notifcation events that can possibly be supported with current
* implementation.
* For capability type ESP_AVRC_RN_CAP_SUPPORTED_EVT, the event set covers the notification
* events selected to be supported under current configuration, The configuration can be
* changed using esp_avrc_tg_set_rn_evt_cap()
*
* @return
* - ESP_OK: success
* - ESP_ERR_INVALID_STATE: if bluetooth stack is not enabled or AVRC TG is not initialized
* - ESP_ERR_INVALID_ARG: if cap is invalid or evt_set is NULL
* - ESP_FAIL: otherwise
*/
esp_err_t esp_avrc_tg_get_rn_evt_cap(esp_avrc_rn_evt_cap_t cap, esp_avrc_rn_evt_cap_mask_t *evt_set);
/**
*
* @brief Set the event notification capabilities on local AVRCP target. The capability is given in a
* bit mask representation in evt_set and must be a subset of allowed event IDs with current
* implementation. This function should be called after esp_avrc_tg_init().
*
* @return
* - ESP_OK: success
* - ESP_ERR_INVALID_STATE: if bluetooth stack is not enabled
* - ESP_ERR_INVALID_ARG: if evt_set is NULL
*
*/
esp_err_t esp_avrc_tg_set_rn_evt_cap(const esp_avrc_rn_evt_cap_mask_t *evt_set);
/**
* @brief Operate on the type esp_avrc_rn_evt_cap_mask_t with regard to a specific event
* @param[in] op: operation requested on the bit mask field
* @param[in] events: pointer to event notification capability bit mask structure
* @param[in] event_id: notification event code
*
* @return For operation ESP_AVRC_BIT_MASK_OP_SET or ESP_AVRC_BIT_MASK_OP_CLEAR, return
* true for a successful operation, otherwise return false
* For operation ESP_AVRC_BIT_MASK_OP_TEST, return true if the corresponding bit
* is set, otherwise false
*
*/
bool esp_avrc_rn_evt_bit_mask_operation(esp_avrc_bit_mask_op_t op, esp_avrc_rn_evt_cap_mask_t *events,
esp_avrc_rn_event_ids_t event_id);
/**
*
* @brief Send RegisterNotification Response to remote AVRCP controller. Local event notification
* capability can be set using esp_avrc_tg_set_rn_evt_cap(),
* in a bit mask representation in evt_set. This function should be called after
* esp_avrc_tg_init()
* @param[in] event_id: notification event ID that remote AVRCP CT registers
* @param[in] rsp: notification response code
* @param[in] param: parameters included in the specific notification
*
* @return
* - ESP_OK: success
* - ESP_ERR_INVALID_STATE: if bluetooth stack is not enabled or AVRC TG is not initialized
* - ESP_ERR_INVALID_ARG: if evt_set is NULL
*
*/
esp_err_t esp_avrc_tg_send_rn_rsp(esp_avrc_rn_event_ids_t event_id, esp_avrc_rn_rsp_t rsp,
esp_avrc_rn_param_t *param);
#ifdef __cplusplus
}
#endif

View file

@ -73,26 +73,6 @@ enum {
BTA_AV_CLOSING_SST
};
/* the call out functions for audio stream */
/* const tBTA_AV_CO_FUNCTS bta_av_a2d_cos =
{
bta_av_co_audio_init,
bta_av_co_audio_disc_res,
bta_av_co_audio_getconfig,
bta_av_co_audio_setconfig,
bta_av_co_audio_open,
bta_av_co_audio_close,
bta_av_co_audio_start,
bta_av_co_audio_stop,
bta_av_co_audio_src_data_path,
bta_av_co_audio_delay
};
*/
tBTA_AV_CO_FUNCTS *p_bta_av_a2d_cos = NULL;
/* ssm action functions for audio stream */
const tBTA_AV_SACT bta_av_a2d_action[] = {
bta_av_do_disc_a2d, /* BTA_AV_DO_DISC */

View file

@ -335,6 +335,8 @@ UINT8 bta_av_rc_create(tBTA_AV_CB *p_cb, UINT8 role, UINT8 shdl, UINT8 lidx)
p_rcb->shdl = shdl;
p_rcb->lidx = lidx;
p_rcb->peer_features = 0;
p_rcb->peer_ct_features = 0;
p_rcb->peer_tg_features = 0;
if (lidx == (BTA_AV_NUM_LINKS + 1)) {
/* this LIDX is reserved for the AVRCP ACP connection */
p_cb->rc_acp_handle = p_rcb->handle;
@ -397,22 +399,13 @@ static tBTA_AV_CODE bta_av_group_navi_supported(UINT8 len, UINT8 *p_data, BOOLEA
static tBTA_AV_CODE bta_av_op_supported(tBTA_AV_RC rc_id, BOOLEAN is_inquiry)
{
tBTA_AV_CODE ret_code = BTA_AV_RSP_NOT_IMPL;
BOOLEAN rc_id_allowed = FALSE;
if (bta_av_cb.p_rc_cos && bta_av_cb.p_rc_cos->rc_cmd) {
rc_id_allowed = bta_av_cb.p_rc_cos->rc_cmd(rc_id);
}
if (p_bta_av_rc_id) {
if (is_inquiry) {
if (p_bta_av_rc_id[rc_id >> 4] & (1 << (rc_id & 0x0F))) {
ret_code = BTA_AV_RSP_IMPL_STBL;
}
} else {
if (p_bta_av_rc_id[rc_id >> 4] & (1 << (rc_id & 0x0F))) {
ret_code = BTA_AV_RSP_ACCEPT;
} else if ((p_bta_av_cfg->rc_pass_rsp == BTA_AV_RSP_INTERIM) && p_bta_av_rc_id_ac) {
if (p_bta_av_rc_id_ac[rc_id >> 4] & (1 << (rc_id & 0x0F))) {
ret_code = BTA_AV_RSP_INTERIM;
}
}
}
if (rc_id_allowed == TRUE) {
ret_code = is_inquiry ? BTA_AV_RSP_IMPL_STBL : BTA_AV_RSP_ACCEPT;
}
return ret_code;
}
@ -529,6 +522,8 @@ void bta_av_rc_opened(tBTA_AV_CB *p_cb, tBTA_AV_DATA *p_data)
bdcpy(rc_open.peer_addr, p_data->rc_conn_chg.peer_addr);
rc_open.peer_features = p_cb->rcb[i].peer_features;
rc_open.peer_ct_features = p_cb->rcb[i].peer_ct_features;
rc_open.peer_tg_features = p_cb->rcb[i].peer_tg_features;
rc_open.sdp_disc_done = TRUE;
rc_open.status = BTA_AV_SUCCESS;
APPL_TRACE_DEBUG("local features:x%x peer_features:x%x", p_cb->features,
@ -687,7 +682,6 @@ void bta_av_rc_free_msg (tBTA_AV_CB *p_cb, tBTA_AV_DATA *p_data)
static tAVRC_STS bta_av_chk_notif_evt_id(tAVRC_MSG_VENDOR *p_vendor)
{
tAVRC_STS status = BTA_AV_STS_NO_RSP;
UINT8 xx;
UINT16 u16;
UINT8 *p = p_vendor->p_vendor_data + 2;
@ -696,15 +690,13 @@ static tAVRC_STS bta_av_chk_notif_evt_id(tAVRC_MSG_VENDOR *p_vendor)
if ((u16 != 5) || (p_vendor->vendor_len != 9)) {
status = AVRC_STS_INTERNAL_ERR;
} else {
/* make sure the player_id is valid */
for (xx = 0; xx < p_bta_av_cfg->num_evt_ids; xx++) {
if (*p == p_bta_av_cfg->p_meta_evt_ids[xx]) {
break;
/* make sure the event_id is valid */
status = AVRC_STS_BAD_PARAM;
if (bta_av_cb.p_rc_cos && bta_av_cb.p_rc_cos->rn_evt_supported) {
if (bta_av_cb.p_rc_cos->rn_evt_supported(*p) == TRUE) {
status = BTA_AV_STS_NO_RSP;
}
}
if (xx == p_bta_av_cfg->num_evt_ids) {
status = AVRC_STS_BAD_PARAM;
}
}
return status;
@ -763,9 +755,12 @@ tBTA_AV_EVT bta_av_proc_meta_cmd(tAVRC_RESPONSE *p_rc_rsp, tBTA_AV_RC_MSG *p_ms
(p_bta_av_cfg->num_co_ids << 2));
} else if (u8 == AVRC_CAP_EVENTS_SUPPORTED) {
*p_ctype = AVRC_RSP_IMPL_STBL;
p_rc_rsp->get_caps.count = p_bta_av_cfg->num_evt_ids;
memcpy(p_rc_rsp->get_caps.param.event_id, p_bta_av_cfg->p_meta_evt_ids,
p_bta_av_cfg->num_evt_ids);
if (bta_av_cb.p_rc_cos && bta_av_cb.p_rc_cos->rn_evt_cap) {
p_rc_rsp->get_caps.count = bta_av_cb.p_rc_cos->rn_evt_cap(
p_rc_rsp->get_caps.param.event_id);
} else {
p_rc_rsp->get_caps.count = 0;
}
} else {
APPL_TRACE_DEBUG("Invalid capability ID: 0x%x", u8);
/* reject - unknown capability ID */
@ -774,7 +769,6 @@ tBTA_AV_EVT bta_av_proc_meta_cmd(tAVRC_RESPONSE *p_rc_rsp, tBTA_AV_RC_MSG *p_ms
}
break;
case AVRC_PDU_REGISTER_NOTIFICATION:
/* make sure the event_id is implemented */
p_rc_rsp->rsp.status = bta_av_chk_notif_evt_id (p_vendor);
@ -783,6 +777,12 @@ tBTA_AV_EVT bta_av_proc_meta_cmd(tAVRC_RESPONSE *p_rc_rsp, tBTA_AV_RC_MSG *p_ms
}
break;
case AVRC_PDU_SET_ABSOLUTE_VOLUME:
p_rc_rsp->rsp.status = BTA_AV_STS_NO_RSP;
break;
default:
APPL_TRACE_WARNING("%s unhandled RC vendor PDU: 0x%x", __FUNCTION__, pdu);
break;
}
}
#else
@ -857,6 +857,10 @@ void bta_av_rc_msg(tBTA_AV_CB *p_cb, tBTA_AV_DATA *p_data)
av.remote_cmd.label = p_data->rc_msg.label;
}
}
/* else if this is a pass thru respone that TG doesn't implement thie command */
else if (p_data->rc_msg.msg.hdr.ctype == AVRC_RSP_NOT_IMPL) {
/* do nothing, no need to setup for callback */
}
/* else if this is a pass thru response */
else if (p_data->rc_msg.msg.hdr.ctype >= AVRC_RSP_ACCEPT) {
/* set up for callback */
@ -1484,15 +1488,15 @@ static void bta_av_acp_sig_timer_cback (TIMER_LIST_ENT *p_tle)
/*******************************************************************************
**
** Function bta_av_check_peer_features
** Function bta_av_check_peer_rc_features
**
** Description check supported features on the peer device from the SDP record
** and return the feature mask
** Description check supported AVRC features on the peer device from the SDP
** record and return the feature mask
**
** Returns tBTA_AV_FEAT peer device feature mask
**
*******************************************************************************/
tBTA_AV_FEAT bta_av_check_peer_features (UINT16 service_uuid)
tBTA_AV_FEAT bta_av_check_peer_rc_features (UINT16 service_uuid, UINT16 *rc_features)
{
tBTA_AV_FEAT peer_features = 0;
tBTA_AV_CB *p_cb = &bta_av_cb;
@ -1501,7 +1505,7 @@ tBTA_AV_FEAT bta_av_check_peer_features (UINT16 service_uuid)
UINT16 peer_rc_version = 0;
UINT16 categories = 0;
APPL_TRACE_DEBUG("bta_av_check_peer_features service_uuid:x%x", service_uuid);
APPL_TRACE_DEBUG("bta_av_check_peer_rc features service_uuid:x%x", service_uuid);
/* loop through all records we found */
while (TRUE) {
/* get next record; if none found, we're done */
@ -1541,7 +1545,12 @@ tBTA_AV_FEAT bta_av_check_peer_features (UINT16 service_uuid)
}
}
}
APPL_TRACE_DEBUG("peer_features:x%x", peer_features);
if (rc_features) {
*rc_features = categories;
}
APPL_TRACE_DEBUG("peer_features:x%x, rc:x%x", peer_features, categories);
return peer_features;
}
@ -1564,6 +1573,8 @@ void bta_av_rc_disc_done(tBTA_AV_DATA *p_data)
tBTA_AV_RC_FEAT rc_feat;
UINT8 rc_handle;
tBTA_AV_FEAT peer_features; /* peer features mask */
UINT16 peer_ct_features; /* peer features mask as controller */
UINT16 peer_tg_features; /* peer features mask as target */
UNUSED(p_data);
APPL_TRACE_DEBUG("bta_av_rc_disc_done disc:x%x", p_cb->disc);
@ -1589,17 +1600,13 @@ void bta_av_rc_disc_done(tBTA_AV_DATA *p_data)
APPL_TRACE_DEBUG("rc_handle %d", rc_handle);
/* check peer version and whether support CT and TG role */
peer_features = bta_av_check_peer_features (UUID_SERVCLASS_AV_REMOTE_CONTROL);
if ((p_cb->features & BTA_AV_FEAT_ADV_CTRL) && ((peer_features & BTA_AV_FEAT_ADV_CTRL) == 0)) {
/* if we support advance control and peer does not, check their support on TG role
* some implementation uses 1.3 on CT ans 1.4 on TG */
peer_features |= bta_av_check_peer_features (UUID_SERVCLASS_AV_REM_CTRL_TARGET);
}
peer_features = bta_av_check_peer_rc_features (UUID_SERVCLASS_AV_REMOTE_CONTROL, &peer_ct_features);
peer_features |= bta_av_check_peer_rc_features (UUID_SERVCLASS_AV_REM_CTRL_TARGET, &peer_tg_features);
p_cb->disc = 0;
utl_freebuf((void **) &p_cb->p_disc_db);
APPL_TRACE_DEBUG("peer_features 0x%x, features 0x%x", peer_features, p_cb->features);
APPL_TRACE_DEBUG("peer_features 0x%x, local features 0x%x", peer_features, p_cb->features);
/* if we have no rc connection */
if (rc_handle == BTA_AV_RC_HANDLE_NONE) {
@ -1611,6 +1618,8 @@ void bta_av_rc_disc_done(tBTA_AV_DATA *p_data)
if (p_lcb) {
rc_handle = bta_av_rc_create(p_cb, AVCT_INT, (UINT8)(p_scb->hdi + 1), p_lcb->lidx);
p_cb->rcb[rc_handle].peer_features = peer_features;
p_cb->rcb[rc_handle].peer_ct_features = peer_ct_features;
p_cb->rcb[rc_handle].peer_tg_features = peer_tg_features;
}
#if (BT_USE_TRACES == TRUE || BT_TRACE_APPL == TRUE)
else {
@ -1631,6 +1640,8 @@ void bta_av_rc_disc_done(tBTA_AV_DATA *p_data)
p_cb->rcb[rc_handle].peer_features = peer_features;
rc_feat.rc_handle = rc_handle;
rc_feat.peer_features = peer_features;
rc_feat.peer_ct_features = peer_ct_features;
rc_feat.peer_tg_features = peer_tg_features;
(*p_cb->p_cback)(BTA_AV_RC_FEAT_EVT, (tBTA_AV *) &rc_feat);
}
}
@ -1665,6 +1676,8 @@ void bta_av_rc_closed(tBTA_AV_DATA *p_data)
rc_close.rc_handle = i;
p_rcb->status &= ~BTA_AV_RC_CONN_MASK;
p_rcb->peer_features = 0;
p_rcb->peer_ct_features = 0;
p_rcb->peer_tg_features = 0;
APPL_TRACE_DEBUG(" shdl:%d, lidx:%d", p_rcb->shdl, p_rcb->lidx);
if (p_rcb->shdl) {
if ((p_rcb->shdl - 1) < BTA_AV_NUM_STRS) {

View file

@ -105,7 +105,9 @@ void BTA_AvDisable(void)
** Returns void
**
*******************************************************************************/
void BTA_AvRegister(tBTA_AV_CHNL chnl, const char *p_service_name, UINT8 app_id, tBTA_AV_DATA_CBACK *p_data_cback, tBTA_AV_CO_FUNCTS *bta_av_cos, UINT8 tsep)
void BTA_AvRegister(tBTA_AV_CHNL chnl, const char *p_service_name, UINT8 app_id,
tBTA_AV_DATA_CBACK *p_data_cback, tBTA_AV_CO_FUNCTS *bta_av_cos,
tBTA_AVRC_CO_FUNCTS *bta_avrc_cos, UINT8 tsep)
{
tBTA_AV_API_REG *p_buf;
@ -122,6 +124,7 @@ void BTA_AvRegister(tBTA_AV_CHNL chnl, const char *p_service_name, UINT8 app_id,
p_buf->app_id = app_id;
p_buf->p_app_data_cback = p_data_cback;
p_buf->bta_av_cos = bta_av_cos;
p_buf->bta_avrc_cos = bta_avrc_cos;
p_buf->tsep = tsep;
bta_sys_sendmsg(p_buf);
}

View file

@ -41,7 +41,7 @@ const UINT32 bta_av_meta_caps_co_ids[] = {
};
/* AVRCP cupported categories */
#define BTA_AV_RC_SUPF_CT (AVRC_SUPF_CT_CAT2)
#define BTA_AV_RC_SUPF_CT (AVRC_SUPF_CT_CAT1)
/* Added to modify
** 1. flush timeout
@ -62,26 +62,11 @@ const UINT16 bta_av_audio_flush_to[] = {
/* Note: Android doesnt support AVRC_SUPF_TG_GROUP_NAVI */
/* Note: if AVRC_SUPF_TG_GROUP_NAVI is set, bta_av_cfg.avrc_group should be TRUE */
#if AVRC_METADATA_INCLUDED == TRUE
#define BTA_AV_RC_SUPF_TG (AVRC_SUPF_TG_CAT1) /* TODO: | AVRC_SUPF_TG_APP_SETTINGS) */
#define BTA_AV_RC_SUPF_TG (AVRC_SUPF_TG_CAT2) /* TODO: | AVRC_SUPF_TG_APP_SETTINGS) */
#else
#define BTA_AV_RC_SUPF_TG (AVRC_SUPF_TG_CAT1)
#define BTA_AV_RC_SUPF_TG (AVRC_SUPF_TG_CAT2)
#endif
/*
* If the number of event IDs is changed in this array, BTA_AV_ NUM_RC_EVT_IDS also needs to be changed.
*/
const UINT8 bta_av_meta_caps_evt_ids[] = {
AVRC_EVT_PLAY_STATUS_CHANGE,
AVRC_EVT_TRACK_CHANGE,
AVRC_EVT_PLAY_POS_CHANGED,
/* TODO: Add support for these events
AVRC_EVT_APP_SETTING_CHANGE,
*/
};
#ifndef BTA_AV_NUM_RC_EVT_IDS
#define BTA_AV_NUM_RC_EVT_IDS (sizeof(bta_av_meta_caps_evt_ids) / sizeof(bta_av_meta_caps_evt_ids[0]))
#endif /* BTA_AV_NUM_RC_EVT_IDS */
/* the MTU for the AVRCP browsing channel */
#ifndef BTA_AV_MAX_RC_BR_MTU
#define BTA_AV_MAX_RC_BR_MTU 1008
@ -105,10 +90,8 @@ const tBTA_AV_CFG bta_av_cfg = {
600, /* AVDTP video transport channel flush timeout */
FALSE, /* TRUE, to accept AVRC 1.3 group nevigation command */
2, /* company id count in p_meta_co_ids */
BTA_AV_NUM_RC_EVT_IDS, /* event id count in p_meta_evt_ids */
BTA_AV_RC_PASS_RSP_CODE,/* the default response code for pass through commands */
bta_av_meta_caps_co_ids,/* the metadata Get Capabilities response for company id */
bta_av_meta_caps_evt_ids,/* the the metadata Get Capabilities response for event id */
NULL, /* the action function table for VDP stream */
NULL, /* action function to register VDP */
{0}, /* Default AVRCP controller name */
@ -117,91 +100,4 @@ const tBTA_AV_CFG bta_av_cfg = {
tBTA_AV_CFG *p_bta_av_cfg = (tBTA_AV_CFG *) &bta_av_cfg;
const UINT16 bta_av_rc_id[] = {
0x0000, /* bit mask: 0=SELECT, 1=UP, 2=DOWN, 3=LEFT,
4=RIGHT, 5=RIGHT_UP, 6=RIGHT_DOWN, 7=LEFT_UP,
8=LEFT_DOWN, 9=ROOT_MENU, 10=SETUP_MENU, 11=CONT_MENU,
12=FAV_MENU, 13=EXIT */
0, /* not used */
0x0000, /* bit mask: 0=0, 1=1, 2=2, 3=3,
4=4, 5=5, 6=6, 7=7,
8=8, 9=9, 10=DOT, 11=ENTER,
12=CLEAR */
0x0000, /* bit mask: 0=CHAN_UP, 1=CHAN_DOWN, 2=PREV_CHAN, 3=SOUND_SEL,
4=INPUT_SEL, 5=DISP_INFO, 6=HELP, 7=PAGE_UP,
8=PAGE_DOWN */
#if (BTA_AV_RC_PASS_RSP_CODE == BTA_AV_RSP_INTERIM)
/* btui_app provides an example of how to leave the decision of rejecting a command or not
* based on which media player is currently addressed (this is only applicable for AVRCP 1.4 or later)
* If the decision is per player for a particular rc_id, the related bit is clear (not set) */
0x0070, /* bit mask: 0=POWER, 1=VOL_UP, 2=VOL_DOWN, 3=MUTE,
4=PLAY, 5=STOP, 6=PAUSE, 7=RECORD,
8=REWIND, 9=FAST_FOR, 10=EJECT, 11=FORWARD,
12=BACKWARD */
#else
#if (defined BTA_AVRCP_FF_RW_SUPPORT) && (BTA_AVRCP_FF_RW_SUPPORT == TRUE)
0x1b70, /* bit mask: 0=POWER, 1=VOL_UP, 2=VOL_DOWN, 3=MUTE,
4=PLAY, 5=STOP, 6=PAUSE, 7=RECORD,
8=REWIND, 9=FAST_FOR, 10=EJECT, 11=FORWARD,
12=BACKWARD */
#else
0x1870, /* bit mask: 0=POWER, 1=VOL_UP, 2=VOL_DOWN, 3=MUTE,
4=PLAY, 5=STOP, 6=PAUSE, 7=RECORD,
8=REWIND, 9=FAST_FOR, 10=EJECT, 11=FORWARD,
12=BACKWARD */
#endif
#endif
0x0000, /* bit mask: 0=ANGLE, 1=SUBPICT */
0, /* not used */
0x0000 /* bit mask: 0=not used, 1=F1, 2=F2, 3=F3,
4=F4, 5=F5 */
};
#if (BTA_AV_RC_PASS_RSP_CODE == BTA_AV_RSP_INTERIM)
const UINT16 bta_av_rc_id_ac[] = {
0x0000, /* bit mask: 0=SELECT, 1=UP, 2=DOWN, 3=LEFT,
4=RIGHT, 5=RIGHT_UP, 6=RIGHT_DOWN, 7=LEFT_UP,
8=LEFT_DOWN, 9=ROOT_MENU, 10=SETUP_MENU, 11=CONT_MENU,
12=FAV_MENU, 13=EXIT */
0, /* not used */
0x0000, /* bit mask: 0=0, 1=1, 2=2, 3=3,
4=4, 5=5, 6=6, 7=7,
8=8, 9=9, 10=DOT, 11=ENTER,
12=CLEAR */
0x0000, /* bit mask: 0=CHAN_UP, 1=CHAN_DOWN, 2=PREV_CHAN, 3=SOUND_SEL,
4=INPUT_SEL, 5=DISP_INFO, 6=HELP, 7=PAGE_UP,
8=PAGE_DOWN */
/* btui_app provides an example of how to leave the decision of rejecting a command or not
* based on which media player is currently addressed (this is only applicable for AVRCP 1.4 or later)
* If the decision is per player for a particular rc_id, the related bit is set */
0x1800, /* bit mask: 0=POWER, 1=VOL_UP, 2=VOL_DOWN, 3=MUTE,
4=PLAY, 5=STOP, 6=PAUSE, 7=RECORD,
8=REWIND, 9=FAST_FOR, 10=EJECT, 11=FORWARD,
12=BACKWARD */
0x0000, /* bit mask: 0=ANGLE, 1=SUBPICT */
0, /* not used */
0x0000 /* bit mask: 0=not used, 1=F1, 2=F2, 3=F3,
4=F4, 5=F5 */
};
UINT16 *p_bta_av_rc_id_ac = (UINT16 *) bta_av_rc_id_ac;
#else
UINT16 *p_bta_av_rc_id_ac = NULL;
#endif
UINT16 *p_bta_av_rc_id = (UINT16 *) bta_av_rc_id;
#endif /* if defined(BTA_AV_INCLUDED) && (BTA_AV_INCLUDED == TRUE) */

View file

@ -534,6 +534,11 @@ static void bta_av_api_register(tBTA_AV_DATA *p_data)
registr.app_id = p_data->api_reg.app_id;
registr.chnl = (tBTA_AV_CHNL)p_data->hdr.layer_specific;
registr.p_bta_av_cos = p_data->api_reg.bta_av_cos;
registr.p_bta_avrc_cos = p_data->api_reg.bta_avrc_cos;
// set the avrc call-out functions
bta_av_cb.p_rc_cos = p_data->api_reg.bta_avrc_cos;
do {
p_scb = bta_av_alloc_scb(registr.chnl);
if (p_scb == NULL) {
@ -619,7 +624,6 @@ static void bta_av_api_register(tBTA_AV_DATA *p_data)
if (registr.chnl == BTA_AV_CHNL_AUDIO) {
/* set up the audio stream control block */
p_scb->p_act_tbl = (const tBTA_AV_ACT *)bta_av_a2d_action;
// p_scb->p_cos = &bta_av_a2d_cos;
p_scb->p_cos = registr.p_bta_av_cos;
p_scb->media_type = AVDT_MEDIA_AUDIO;
cs.cfg.psc_mask = AVDT_PSC_TRANS;

View file

@ -174,6 +174,7 @@ typedef struct {
UINT8 tsep; // local SEP type
tBTA_AV_DATA_CBACK *p_app_data_cback;
tBTA_AV_CO_FUNCTS *bta_av_cos;
tBTA_AVRC_CO_FUNCTS *bta_avrc_cos;
} tBTA_AV_API_REG;
@ -473,6 +474,8 @@ typedef struct {
UINT8 shdl; /* stream handle (hdi + 1) */
UINT8 lidx; /* (index+1) to LCB */
tBTA_AV_FEAT peer_features; /* peer features mask */
UINT16 peer_ct_features;
UINT16 peer_tg_features;
} tBTA_AV_RCB;
#define BTA_AV_NUM_RCB (BTA_AV_NUM_STRS + 2)
@ -509,6 +512,7 @@ typedef struct {
tBTA_AV_FEAT features; /* features mask */
tBTA_SEC sec_mask; /* security mask */
tBTA_AV_HNDL handle; /* the handle for SDP activity */
tBTA_AVRC_CO_FUNCTS *p_rc_cos; /* AVRCP call-out functions */
BOOLEAN disabling; /* TRUE if api disabled called */
UINT8 disc; /* (hdi+1) or (rc_handle|BTA_AV_CHNL_MSK) if p_disc_db is in use */
UINT8 state; /* state machine state */
@ -544,12 +548,7 @@ extern tBTA_AV_CB *bta_av_cb_ptr;
/* config struct */
extern tBTA_AV_CFG *p_bta_av_cfg;
/* rc id config struct */
extern UINT16 *p_bta_av_rc_id;
extern UINT16 *p_bta_av_rc_id_ac;
extern const tBTA_AV_SACT bta_av_a2d_action[];
// extern const tBTA_AV_CO_FUNCTS bta_av_a2d_cos;
extern const tBTA_AV_SACT bta_av_vdp_action[];
extern tAVDT_CTRL_CBACK *const bta_av_dt_cback[];
extern void bta_av_stream_data_cback(UINT8 handle, BT_HDR *p_pkt, UINT32 time_stamp, UINT8 m_pt);

View file

@ -279,20 +279,35 @@ typedef void *(*tBTA_AV_CO_DATAPATH) (tBTA_AV_CODEC codec_type,
UINT32 *p_len, UINT32 *p_timestamp);
typedef void (*tBTA_AV_CO_DELAY) (tBTA_AV_HNDL hndl, UINT16 delay);
/// AVRCP call-out function
// check whether PASSTHROUGH command is supported
typedef BOOLEAN (*tBTA_AVRC_CO_CMD_ALLOWED) (tBTA_AV_RC rc_id);
// get the event notification capabilities
typedef UINT8 (*tBTA_AVRC_CO_RN_EVT_CAP) (UINT8 *event_ids);
// check whether the event_id is supported
typedef BOOLEAN (*tBTA_AVRC_CO_RN_EVT_SUPPORTED) (UINT8 event_id);
/* the call-out functions for one stream */
typedef struct {
tBTA_AV_CO_INIT init;
tBTA_AV_CO_DISC_RES disc_res;
tBTA_AV_CO_GETCFG getcfg;
tBTA_AV_CO_SETCFG setcfg;
tBTA_AV_CO_OPEN open;
tBTA_AV_CO_CLOSE close;
tBTA_AV_CO_START start;
tBTA_AV_CO_STOP stop;
tBTA_AV_CO_DATAPATH data;
tBTA_AV_CO_DELAY delay;
tBTA_AV_CO_INIT init;
tBTA_AV_CO_DISC_RES disc_res;
tBTA_AV_CO_GETCFG getcfg;
tBTA_AV_CO_SETCFG setcfg;
tBTA_AV_CO_OPEN open;
tBTA_AV_CO_CLOSE close;
tBTA_AV_CO_START start;
tBTA_AV_CO_STOP stop;
tBTA_AV_CO_DATAPATH data;
tBTA_AV_CO_DELAY delay;
} tBTA_AV_CO_FUNCTS;
/* the call-out functions for AVRCP */
typedef struct {
tBTA_AVRC_CO_CMD_ALLOWED rc_cmd;
tBTA_AVRC_CO_RN_EVT_CAP rn_evt_cap;
tBTA_AVRC_CO_RN_EVT_SUPPORTED rn_evt_supported;
} tBTA_AVRC_CO_FUNCTS;
typedef UINT8 tBTA_AV_EVT;
/* Event associated with BTA_AV_ENABLE_EVT */
@ -307,6 +322,7 @@ typedef struct {
UINT8 app_id; /* ID associated with call to BTA_AvRegister() */
tBTA_AV_STATUS status;
tBTA_AV_CO_FUNCTS *p_bta_av_cos;
tBTA_AVRC_CO_FUNCTS *p_bta_avrc_cos;
} tBTA_AV_REGISTER;
/* data associated with BTA_AV_OPEN_EVT */
@ -377,6 +393,8 @@ typedef struct {
UINT8 rc_handle;
BOOLEAN sdp_disc_done;
tBTA_AV_FEAT peer_features;
UINT16 peer_ct_features;
UINT16 peer_tg_features;
BD_ADDR peer_addr;
tBTA_AV_STATUS status;
} tBTA_AV_RC_OPEN;
@ -391,6 +409,8 @@ typedef struct {
typedef struct {
UINT8 rc_handle;
tBTA_AV_FEAT peer_features;
UINT16 peer_ct_features;
UINT16 peer_tg_features;
} tBTA_AV_RC_FEAT;
/* data associated with BTA_AV_REMOTE_CMD_EVT */
@ -519,10 +539,8 @@ typedef struct {
UINT16 video_flush_to; /* AVDTP video transport channel flush timeout */
BOOLEAN avrc_group; /* TRUE, to accept AVRC 1.3 group nevigation command */
UINT8 num_co_ids; /* company id count in p_meta_co_ids */
UINT8 num_evt_ids; /* event id count in p_meta_evt_ids */
tBTA_AV_CODE rc_pass_rsp; /* the default response code for pass through commands */
const UINT32 *p_meta_co_ids;/* the metadata Get Capabilities response for company id */
const UINT8 *p_meta_evt_ids;/* the the metadata Get Capabilities response for event id */
const tBTA_AV_ACT *p_act_tbl;/* the action function table for VDP stream */
tBTA_AV_REG *p_reg; /* action function to register VDP */
char avrc_controller_name[BTA_SERVICE_NAME_LEN]; /* Default AVRCP controller name */
@ -580,7 +598,9 @@ void BTA_AvDisable(void);
**
*******************************************************************************/
void BTA_AvRegister(tBTA_AV_CHNL chnl, const char *p_service_name,
UINT8 app_id, tBTA_AV_DATA_CBACK *p_data_cback, tBTA_AV_CO_FUNCTS *bta_av_cos, UINT8 tsep);
UINT8 app_id, tBTA_AV_DATA_CBACK *p_data_cback,
tBTA_AV_CO_FUNCTS *bta_av_cos, tBTA_AVRC_CO_FUNCTS *bta_avrc_cos,
UINT8 tsep);
/*******************************************************************************
**

View file

@ -78,7 +78,8 @@ static btc_func_t profile_tab[BTC_PID_NUM] = {
[BTC_PID_PRF_QUE] = {btc_profile_queue_handler, NULL },
#if BTC_AV_INCLUDED
[BTC_PID_A2DP] = {btc_a2dp_call_handler, btc_a2dp_cb_handler },
[BTC_PID_AVRC] = {btc_avrc_call_handler, NULL },
[BTC_PID_AVRC_CT] = {btc_avrc_ct_call_handler, NULL },
[BTC_PID_AVRC_TG] = {btc_avrc_tg_call_handler, NULL },
#endif /* #if BTC_AV_INCLUDED */
#if CONFIG_BT_SPP_ENABLED
[BTC_PID_SPP] = {btc_spp_call_handler, btc_spp_cb_handler },

View file

@ -57,7 +57,8 @@ typedef enum {
BTC_PID_GAP_BT,
BTC_PID_PRF_QUE,
BTC_PID_A2DP,
BTC_PID_AVRC,
BTC_PID_AVRC_CT,
BTC_PID_AVRC_TG,
BTC_PID_SPP,
#if BTC_HF_CLIENT_INCLUDED
BTC_PID_HF_CLIENT,

View file

@ -148,6 +148,7 @@ static void btc_av_event_free_data(btc_sm_event_t event, void *p_data);
*************************************************************************/
extern tBTA_AV_CO_FUNCTS bta_av_a2d_cos;
extern tBTA_AVRC_CO_FUNCTS bta_avrc_cos;
/*****************************************************************************
** Local helper functions
******************************************************************************/
@ -1233,7 +1234,7 @@ bt_status_t btc_av_execute_service(BOOLEAN b_enable, UINT8 tsep)
| 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, BTC_AV_SERVICE_NAME, 0, bte_av_media_callback, &bta_av_a2d_cos, tsep);
BTA_AvRegister(BTA_AV_CHNL_AUDIO, BTC_AV_SERVICE_NAME, 0, bte_av_media_callback, &bta_av_a2d_cos, &bta_avrc_cos, tsep);
} else {
BTA_AvDeregister(btc_av_cb.bta_handle);
BTA_AvDisable();

View file

@ -0,0 +1,106 @@
/******************************************************************************
*
* This is the AVRC call-out function implementation for BTC.
*
******************************************************************************/
// Copyright 2015-2018 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.
/*****************************************************************************
*
* Filename: bta_avrc_co.c
*
* Description: Bluetooth AVRC implementation
*
*****************************************************************************/
#include <stdint.h>
#include "common/bt_target.h"
#include "bta/bta_sys.h"
#include "bta/bta_av_api.h"
#include "btc_avrc.h"
#if BTC_AV_INCLUDED
/*******************************************************************************
**
** Function bta_avrc_co_cmd_allowed
**
** Description Check if local AVRCP TG configuration supports a specific
** PASSTHROUGH command with the given operation_id
**
** Returns TRUE if operation_id is supported, FALSE otherwise
**
*******************************************************************************/
BOOLEAN bta_avrc_co_cmd_allowed(tBTA_AV_RC rc_id)
{
if (rc_id >= BTA_AV_VENDOR) {
return FALSE;
}
const uint16_t *rc_cmd = btc_avrc_tg_get_supported_command();
if (rc_cmd[rc_id >> 4] & ((uint16_t)1 << (rc_id & 0x0F))) {
return TRUE;
} else {
return FALSE;
}
}
/*******************************************************************************
**
** Function bta_avrc_co_rn_evt_cap
**
** Description get the event notifcation capabilities on AVRCP target
**
** Returns number of event_ids supported
**
*******************************************************************************/
UINT8 bta_avrc_co_rn_evt_cap(UINT8 *event_ids)
{
if (event_ids == 0) {
return 0;
}
UINT16 event_bits = btc_avrc_tg_get_rn_supported_evt();
UINT8 count = 0;
for (UINT8 i = 0; i < 16; ++i, event_bits >>= 1) {
if (event_bits & 0x01) {
event_ids[count++] = i;
}
}
return count;
}
/*******************************************************************************
**
** Function bta_avrc_co_evt_supported
**
** Description Check if local AVRCP TG configuration supports the given
** event_id
**
** Returns TRUE if operation_id is supported, FALSE otherwise
**
*******************************************************************************/
BOOLEAN bta_avrc_co_rn_evt_supported(UINT8 event_id)
{
return btc_avrc_tg_rn_evt_supported(event_id) ?
TRUE : FALSE;
}
/* the call out functions for AVRC */
tBTA_AVRC_CO_FUNCTS bta_avrc_cos = {
bta_avrc_co_cmd_allowed,
bta_avrc_co_rn_evt_cap,
bta_avrc_co_rn_evt_supported,
};
#endif /* #if BTC_AV_INCLUDED */

File diff suppressed because it is too large Load diff

View file

@ -22,6 +22,8 @@
#include "common/bt_defs.h"
#include "stack/bt_types.h"
#include "bta/bta_av_api.h"
#include "btc/btc_task.h"
#include "esp_avrc_api.h"
#if (BTC_AV_INCLUDED == TRUE)
#ifndef BTC_AVRC_TGT_INCLUDED
@ -29,13 +31,14 @@
#endif
typedef enum {
BTC_AVRC_CTRL_API_INIT_EVT = 0,
BTC_AVRC_CTRL_API_DEINIT_EVT,
BTC_AVRC_CT_API_INIT_EVT = 0,
BTC_AVRC_CT_API_DEINIT_EVT,
BTC_AVRC_CTRL_API_SND_PTCMD_EVT,
BTC_AVRC_STATUS_API_SND_META_EVT,
BTC_AVRC_STATUS_API_SND_PLAY_STATUS_EVT,
BTC_AVRC_STATUS_API_SND_GET_RN_CAPS_EVT,
BTC_AVRC_NOTIFY_API_SND_REG_NOTIFY_EVT,
BTC_AVRC_CTRL_API_SET_PLAYER_SETTING_EVT
BTC_AVRC_CTRL_API_SND_SET_PLAYER_SETTING_EVT
} btc_avrc_act_t;
typedef struct {
@ -61,18 +64,40 @@ typedef struct {
uint8_t value_id;
} ps_cmd_t;
typedef struct {
uint8_t tl;
} get_caps_cmd_t;
/* btc_avrc_args_t */
typedef union {
pt_cmd_t pt_cmd;
md_cmd_t md_cmd;
rn_cmd_t rn_cmd;
ps_cmd_t ps_cmd;
get_caps_cmd_t get_caps_cmd;
} btc_avrc_args_t;
/** BT-RC Controller callback structure. */
typedef void (* btrc_passthrough_rsp_callback) (int id, int key_state);
/* btc_avrc_tg_act_t */
typedef enum {
BTC_AVRC_TG_API_INIT_EVT = 0,
BTC_AVRC_TG_API_DEINIT_EVT,
BTC_AVRC_TG_API_SET_RN_SUPPORTED_EVT,
BTC_AVRC_TG_API_SET_PSTH_SUPPORTED_CMD_EVT,
BTC_AVRC_TG_API_SEND_RN_RSP_EVT,
} btc_avrc_tg_act_t;
typedef void (* btrc_connection_state_callback) (bool state, bt_bdaddr_t *bd_addr);
typedef struct {
esp_avrc_rn_event_ids_t event_id;
esp_avrc_rn_rsp_t rsp;
esp_avrc_rn_param_t param;
} rn_rsp_t;
/* btc_avrc_tg_args_t */
typedef union {
rn_rsp_t rn_rsp; /* BTC_AVRC_TG_API_SEND_RN_RSP_EVT */
uint16_t set_rn_evt; /* BTC_AVRC_TG_API_SET_RN_SUPPORTED_EVT */
uint16_t *set_psth_cmd; /* BTC_AVRC_TG_API_SET_PSTH_SUPPORTED_CMD_EVT */
} btc_avrc_tg_args_t;
void btc_rc_handler(tBTA_AV_EVT event, tBTA_AV *p_data);
@ -81,7 +106,24 @@ BOOLEAN btc_rc_get_connected_peer(BD_ADDR peer_addr);
/*******************************************************************************
** BTC AVRC API
********************************************************************************/
void btc_avrc_call_handler(btc_msg_t *msg);
void btc_avrc_ct_call_handler(btc_msg_t *msg);
void btc_avrc_tg_call_handler(btc_msg_t *msg);
void btc_avrc_tg_arg_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src);
bool btc_avrc_tg_init_p(void);
bool btc_avrc_ct_init_p(void);
bool btc_avrc_tg_connected_p(void);
bool btc_avrc_ct_connected_p(void);
const uint16_t *btc_avrc_tg_get_supported_command(void);
const uint16_t *btc_avrc_tg_get_allowed_command(void);
bool btc_avrc_tg_check_supported_command(const uint16_t *cmd_set);
uint16_t btc_avrc_tg_get_rn_allowed_evt(void);
uint16_t btc_avrc_tg_get_rn_supported_evt(void);
bool btc_avrc_tg_check_rn_supported_evt(uint16_t evt_set);
bool btc_avrc_tg_rn_evt_supported(uint8_t event_id);
bool btc_avrc_ct_rn_evt_supported(uint8_t event_id);
#endif ///BTC_AV_INCLUDED == TRUE

View file

@ -237,6 +237,22 @@ static tAVRC_STS avrc_bld_get_element_attr_cmd (tAVRC_GET_ELEM_ATTRS_CMD *p_cmd,
return AVRC_STS_NO_ERROR;
}
static tAVRC_STS avrc_bld_get_caps_cmd(tAVRC_GET_CAPS_CMD *p_cmd, BT_HDR *p_pkt)
{
UINT8 *p_data, *p_start;
AVRC_TRACE_API("avrc_bld_get_caps");
/* get the existing length, if any, and also the num attributes */
// Set the notify value
p_start = (UINT8 *)(p_pkt + 1) + p_pkt->offset;
p_data = p_start + 2; /* pdu + rsvd */
/* add fixed length 1 */
UINT16_TO_BE_STREAM(p_data, 1);
/* capability id */
UINT8_TO_BE_STREAM(p_data, p_cmd->capability_id);
p_pkt->len = (p_data - p_start);
return AVRC_STS_NO_ERROR;
}
/*******************************************************************************
**
** Function AVRC_BldCommand
@ -296,6 +312,9 @@ tAVRC_STS AVRC_BldCommand( tAVRC_COMMAND *p_cmd, BT_HDR **pp_pkt)
case AVRC_PDU_REGISTER_NOTIFICATION: /* 0x31 */
status = avrc_bld_register_change_notfn(p_cmd->reg_notif.event_id, p_cmd->reg_notif.param, p_pkt);
break;
case AVRC_PDU_GET_CAPABILITIES:
status = avrc_bld_get_caps_cmd(&p_cmd->get_caps, p_pkt);
break;
}
if (alloc && (status != AVRC_STS_NO_ERROR) ) {

View file

@ -593,6 +593,10 @@ static tAVRC_STS avrc_bld_notify_rsp (tAVRC_REG_NOTIF_RSP *p_rsp, BT_HDR *p_pkt)
}
break;
case AVRC_EVT_VOLUME_CHANGE:
UINT8_TO_BE_STREAM(p_data, p_rsp->param.volume);
len = 2;
break;
default:
status = AVRC_STS_BAD_PARAM;
AVRC_TRACE_ERROR("unknown event_id");
@ -652,6 +656,31 @@ tAVRC_STS avrc_bld_group_navigation_rsp (UINT16 navi_id, BT_HDR *p_pkt)
return AVRC_STS_NO_ERROR;
}
/*******************************************************************************
**
** Function avrc_bld_set_absolute_volume_rsp
**
** Description This function builds the Set Absolute Volume command
** response
**
** Returns AVRC_STS_NO_ERROR, if the response is built successfully
** Otherwise, the error code.
**
*******************************************************************************/
tAVRC_STS avrc_bld_set_absolute_volume_rsp(tAVRC_SET_VOLUME_RSP *p_rsp, BT_HDR *p_pkt)
{
UINT8 *p_data, *p_start;
p_start = (UINT8 *)(p_pkt + 1) + p_pkt->offset;
p_data = p_start + 2; /* pdu + rsvd */
UINT16_TO_BE_STREAM(p_data, 1); /* fixed length 1 */
UINT8_TO_BE_STREAM(p_data, p_rsp->volume);
p_pkt->len = (p_data - p_start);
return AVRC_STS_NO_ERROR;
}
/*******************************************************************************
**
** Function avrc_bld_rejected_rsp
@ -842,9 +871,12 @@ tAVRC_STS AVRC_BldResponse( UINT8 handle, tAVRC_RESPONSE *p_rsp, BT_HDR **pp_pkt
status = avrc_bld_next_rsp(&p_rsp->continu, p_pkt);
break;
case AVRC_PDU_ABORT_CONTINUATION_RSP: /* 0x41 */
case AVRC_PDU_ABORT_CONTINUATION_RSP: /* 0x41 */
status = avrc_bld_next_rsp(&p_rsp->abort, p_pkt);
break;
case AVRC_PDU_SET_ABSOLUTE_VOLUME: /* 0x50 */
status = avrc_bld_set_absolute_volume_rsp(&p_rsp->volume, p_pkt);
break;
}
if (alloc && (status != AVRC_STS_NO_ERROR) ) {

View file

@ -90,9 +90,28 @@ static tAVRC_STS avrc_pars_vendor_rsp(tAVRC_MSG_VENDOR *p_msg, tAVRC_RESPONSE *p
p_result->reg_notif.event_id = eventid;
BE_STREAM_TO_UINT8 (p_result->reg_notif.param.volume, p);
}
AVRC_TRACE_DEBUG("avrc_pars_vendor_rsp PDU reg notif response:event %x, volume %x", eventid,
p_result->reg_notif.param.volume);
// todo: parse the response for other event_ids
AVRC_TRACE_DEBUG("avrc_pars_vendor_rsp PDU reg notif response:event 0x%x", eventid);
break;
#endif /* (AVRC_ADV_CTRL_INCLUDED == TRUE) */
case AVRC_PDU_GET_CAPABILITIES: /* 0x10 */
BE_STREAM_TO_UINT8 (p_result->get_caps.capability_id, p);
BE_STREAM_TO_UINT8 (p_result->get_caps.count, p);
if (p_result->get_caps.capability_id == AVRC_CAP_EVENTS_SUPPORTED) {
if (p_result->get_caps.count > AVRC_CAP_MAX_NUM_EVT_ID) {
status = AVRC_STS_INTERNAL_ERR;
} else {
BE_STREAM_TO_ARRAY(p, p_result->get_caps.param.event_id, p_result->get_caps.count);
}
} else if (p_result->get_caps.capability_id == AVRC_CAP_COMPANY_ID) {
if (p_result->get_caps.count > AVRC_CAP_MAX_NUM_COMP_ID) {
status = AVRC_STS_INTERNAL_ERR;
} else {
for (int i = 0; i < p_result->get_caps.count; ++i) {
BE_STREAM_TO_UINT24(p_result->get_caps.param.company_id[i], p);
}
}
}
break;
default:
status = AVRC_STS_BAD_CMD;
@ -112,12 +131,10 @@ static tAVRC_STS avrc_pars_vendor_rsp(tAVRC_MSG_VENDOR *p_msg, tAVRC_RESPONSE *p
** Otherwise, the error code defined by AVRCP 1.4
**
*******************************************************************************/
tAVRC_STS AVRC_ParsResponse (tAVRC_MSG *p_msg, tAVRC_RESPONSE *p_result, UINT8 *p_buf, UINT16 buf_len)
tAVRC_STS AVRC_ParsResponse (tAVRC_MSG *p_msg, tAVRC_RESPONSE *p_result)
{
tAVRC_STS status = AVRC_STS_INTERNAL_ERR;
UINT16 id;
UNUSED(p_buf);
UNUSED(buf_len);
if (p_msg && p_result) {
switch (p_msg->hdr.opcode) {

View file

@ -249,6 +249,9 @@ static tAVRC_STS avrc_pars_vendor_cmd(tAVRC_MSG_VENDOR *p_msg, tAVRC_COMMAND *p_
case AVRC_PDU_SET_ABSOLUTE_VOLUME: {
if (len != 1) {
status = AVRC_STS_INTERNAL_ERR;
} else {
BE_STREAM_TO_UINT8 (p_result->volume.volume, p);
p_result->volume.volume &= 0x7F; // remove the top bit
}
break;
}

View file

@ -30,7 +30,7 @@
#if (defined(AVRC_INCLUDED) && AVRC_INCLUDED == TRUE)
#ifndef SDP_AVRCP_1_4
#define SDP_AVRCP_1_4 FALSE
#define SDP_AVRCP_1_4 TRUE
#endif
#ifndef SDP_AVCTP_1_4

View file

@ -58,6 +58,7 @@ BOOLEAN AVRC_IsValidAvcType(UINT8 pdu_id, UINT8 avc_type)
case AVRC_PDU_INFORM_BATTERY_STAT_OF_CT: /* 0x18 */
case AVRC_PDU_REQUEST_CONTINUATION_RSP: /* 0x40 */
case AVRC_PDU_ABORT_CONTINUATION_RSP: /* 0x41 */
case AVRC_PDU_SET_ABSOLUTE_VOLUME: /* 0x50 */
if (avc_type == AVRC_CMD_CTRL) {
result = TRUE;
}

View file

@ -590,8 +590,7 @@ extern tAVRC_STS AVRC_ParsCommand (tAVRC_MSG *p_msg, tAVRC_COMMAND *p_result,
** Otherwise, the error code defined by AVRCP 1.4
**
*******************************************************************************/
extern tAVRC_STS AVRC_ParsResponse (tAVRC_MSG *p_msg, tAVRC_RESPONSE *p_result,
UINT8 *p_buf, UINT16 buf_len);
extern tAVRC_STS AVRC_ParsResponse (tAVRC_MSG *p_msg, tAVRC_RESPONSE *p_result);
/*******************************************************************************
**

View file

@ -834,7 +834,7 @@ typedef union {
#define AVRC_IS_VALID_CAP_ID(a) (((a == AVRC_CAP_COMPANY_ID) || (a == AVRC_CAP_EVENTS_SUPPORTED)) ? TRUE : FALSE)
#define AVRC_IS_VALID_EVENT_ID(a) (((a >= AVRC_EVT_PLAY_STATUS_CHANGE) && \
(a <= AVRC_EVT_APP_SETTING_CHANGE)) ? TRUE : FALSE)
(a <= AVRC_EVT_VOLUME_CHANGE)) ? TRUE : FALSE)
#define AVRC_IS_VALID_ATTRIBUTE(a) (((((a > 0) && a <= AVRC_PLAYER_SETTING_SCAN)) || \
(a >= AVRC_PLAYER_SETTING_LOW_MENU_EXT)) ? TRUE : FALSE)

View file

@ -25,15 +25,31 @@
#include "freertos/task.h"
#include "driver/i2s.h"
#include "sys/lock.h"
// AVRCP used transaction label
#define APP_RC_CT_TL_GET_CAPS (0)
#define APP_RC_CT_TL_GET_META_DATA (1)
#define APP_RC_CT_TL_RN_TRACK_CHANGE (2)
#define APP_RC_CT_TL_RN_PLAYBACK_CHANGE (3)
#define APP_RC_CT_TL_RN_PLAY_POS_CHANGE (4)
/* a2dp event handler */
static void bt_av_hdl_a2d_evt(uint16_t event, void *p_param);
/* avrc event handler */
static void bt_av_hdl_avrc_evt(uint16_t event, void *p_param);
/* avrc CT event handler */
static void bt_av_hdl_avrc_ct_evt(uint16_t event, void *p_param);
/* avrc TG event handler */
static void bt_av_hdl_avrc_tg_evt(uint16_t event, void *p_param);
static uint32_t m_pkt_cnt = 0;
static esp_a2d_audio_state_t m_audio_state = ESP_A2D_AUDIO_STATE_STOPPED;
static const char *m_a2d_conn_state_str[] = {"Disconnected", "Connecting", "Connected", "Disconnecting"};
static const char *m_a2d_audio_state_str[] = {"Suspended", "Stopped", "Started"};
static uint32_t s_pkt_cnt = 0;
static esp_a2d_audio_state_t s_audio_state = ESP_A2D_AUDIO_STATE_STOPPED;
static const char *s_a2d_conn_state_str[] = {"Disconnected", "Connecting", "Connected", "Disconnecting"};
static const char *s_a2d_audio_state_str[] = {"Suspended", "Stopped", "Started"};
static esp_avrc_rn_evt_cap_mask_t s_avrc_peer_rn_cap;
static _lock_t s_volume_lock;
static xTaskHandle s_vcs_task_hdl = NULL;
static uint8_t s_volume = 0;
static bool s_volume_notify;
/* callback for A2DP sink */
void bt_app_a2d_cb(esp_a2d_cb_event_t event, esp_a2d_cb_param_t *param)
@ -55,8 +71,8 @@ void bt_app_a2d_data_cb(const uint8_t *data, uint32_t len)
{
size_t bytes_written;
i2s_write(0, data, len, &bytes_written, portMAX_DELAY);
if (++m_pkt_cnt % 100 == 0) {
ESP_LOGI(BT_AV_TAG, "Audio packet count %u", m_pkt_cnt);
if (++s_pkt_cnt % 100 == 0) {
ESP_LOGI(BT_AV_TAG, "Audio packet count %u", s_pkt_cnt);
}
}
@ -64,10 +80,6 @@ void bt_app_alloc_meta_buffer(esp_avrc_ct_cb_param_t *param)
{
esp_avrc_ct_cb_param_t *rc = (esp_avrc_ct_cb_param_t *)(param);
uint8_t *attr_text = (uint8_t *) malloc (rc->meta_rsp.attr_length + 1);
if(!attr_text) {
ESP_LOGI(BT_AV_TAG, "malloc failed ");
return;
}
memcpy(attr_text, rc->meta_rsp.attr_text, rc->meta_rsp.attr_length);
attr_text[rc->meta_rsp.attr_length] = 0;
@ -83,12 +95,29 @@ void bt_app_rc_ct_cb(esp_avrc_ct_cb_event_t event, esp_avrc_ct_cb_param_t *param
case ESP_AVRC_CT_CONNECTION_STATE_EVT:
case ESP_AVRC_CT_PASSTHROUGH_RSP_EVT:
case ESP_AVRC_CT_CHANGE_NOTIFY_EVT:
case ESP_AVRC_CT_REMOTE_FEATURES_EVT: {
bt_app_work_dispatch(bt_av_hdl_avrc_evt, event, param, sizeof(esp_avrc_ct_cb_param_t), NULL);
case ESP_AVRC_CT_REMOTE_FEATURES_EVT:
case ESP_AVRC_CT_GET_RN_CAPABILITIES_RSP_EVT: {
bt_app_work_dispatch(bt_av_hdl_avrc_ct_evt, event, param, sizeof(esp_avrc_ct_cb_param_t), NULL);
break;
}
default:
ESP_LOGE(BT_AV_TAG, "Invalid AVRC event: %d", event);
ESP_LOGE(BT_RC_CT_TAG, "Invalid AVRC event: %d", event);
break;
}
}
void bt_app_rc_tg_cb(esp_avrc_tg_cb_event_t event, esp_avrc_tg_cb_param_t *param)
{
switch (event) {
case ESP_AVRC_TG_CONNECTION_STATE_EVT:
case ESP_AVRC_TG_REMOTE_FEATURES_EVT:
case ESP_AVRC_TG_PASSTHROUGH_CMD_EVT:
case ESP_AVRC_TG_SET_ABSOLUTE_VOLUME_CMD_EVT:
case ESP_AVRC_TG_REGISTER_NOTIFICATION_EVT:
bt_app_work_dispatch(bt_av_hdl_avrc_tg_evt, event, param, sizeof(esp_avrc_tg_cb_param_t), NULL);
break;
default:
ESP_LOGE(BT_RC_TG_TAG, "Invalid AVRC event: %d", event);
break;
}
}
@ -102,15 +131,20 @@ static void bt_av_hdl_a2d_evt(uint16_t event, void *p_param)
a2d = (esp_a2d_cb_param_t *)(p_param);
uint8_t *bda = a2d->conn_stat.remote_bda;
ESP_LOGI(BT_AV_TAG, "A2DP connection state: %s, [%02x:%02x:%02x:%02x:%02x:%02x]",
m_a2d_conn_state_str[a2d->conn_stat.state], bda[0], bda[1], bda[2], bda[3], bda[4], bda[5]);
s_a2d_conn_state_str[a2d->conn_stat.state], bda[0], bda[1], bda[2], bda[3], bda[4], bda[5]);
if (a2d->conn_stat.state == ESP_A2D_CONNECTION_STATE_DISCONNECTED) {
esp_bt_gap_set_scan_mode(ESP_BT_CONNECTABLE, ESP_BT_GENERAL_DISCOVERABLE);
} else if (a2d->conn_stat.state == ESP_A2D_CONNECTION_STATE_CONNECTED){
esp_bt_gap_set_scan_mode(ESP_BT_NON_CONNECTABLE, ESP_BT_NON_DISCOVERABLE);
}
break;
}
case ESP_A2D_AUDIO_STATE_EVT: {
a2d = (esp_a2d_cb_param_t *)(p_param);
ESP_LOGI(BT_AV_TAG, "A2DP audio state: %s", m_a2d_audio_state_str[a2d->audio_stat.state]);
m_audio_state = a2d->audio_stat.state;
ESP_LOGI(BT_AV_TAG, "A2DP audio state: %s", s_a2d_audio_state_str[a2d->audio_stat.state]);
s_audio_state = a2d->audio_stat.state;
if (ESP_A2D_AUDIO_STATE_STARTED == a2d->audio_stat.state) {
m_pkt_cnt = 0;
s_pkt_cnt = 0;
}
break;
}
@ -130,7 +164,7 @@ static void bt_av_hdl_a2d_evt(uint16_t event, void *p_param)
}
i2s_set_clk(0, sample_rate, 16, 2);
ESP_LOGI(BT_AV_TAG, "Configure audio player 0x%x-0x%x-0x%x-0x%x",
ESP_LOGI(BT_AV_TAG, "Configure audio player %x-%x-%x-%x",
a2d->audio_cfg.mcc.cie.sbc[0],
a2d->audio_cfg.mcc.cie.sbc[1],
a2d->audio_cfg.mcc.cie.sbc[2],
@ -145,57 +179,182 @@ static void bt_av_hdl_a2d_evt(uint16_t event, void *p_param)
}
}
static void bt_av_new_track()
static void bt_av_new_track(void)
{
//Register notifications and request metadata
esp_avrc_ct_send_metadata_cmd(0, ESP_AVRC_MD_ATTR_TITLE | ESP_AVRC_MD_ATTR_ARTIST | ESP_AVRC_MD_ATTR_ALBUM | ESP_AVRC_MD_ATTR_GENRE);
esp_avrc_ct_send_register_notification_cmd(1, ESP_AVRC_RN_TRACK_CHANGE, 0);
// request metadata
uint8_t attr_mask = ESP_AVRC_MD_ATTR_TITLE | ESP_AVRC_MD_ATTR_ARTIST | ESP_AVRC_MD_ATTR_ALBUM | ESP_AVRC_MD_ATTR_GENRE;
esp_avrc_ct_send_metadata_cmd(APP_RC_CT_TL_GET_META_DATA, attr_mask);
// register notification if peer support the event_id
if (esp_avrc_rn_evt_bit_mask_operation(ESP_AVRC_BIT_MASK_OP_TEST, &s_avrc_peer_rn_cap,
ESP_AVRC_RN_TRACK_CHANGE)) {
esp_avrc_ct_send_register_notification_cmd(APP_RC_CT_TL_RN_TRACK_CHANGE, ESP_AVRC_RN_TRACK_CHANGE, 0);
}
}
void bt_av_notify_evt_handler(uint8_t event_id, uint32_t event_parameter)
static void bt_av_playback_changed(void)
{
if (esp_avrc_rn_evt_bit_mask_operation(ESP_AVRC_BIT_MASK_OP_TEST, &s_avrc_peer_rn_cap,
ESP_AVRC_RN_PLAY_STATUS_CHANGE)) {
esp_avrc_ct_send_register_notification_cmd(APP_RC_CT_TL_RN_PLAYBACK_CHANGE, ESP_AVRC_RN_PLAY_STATUS_CHANGE, 0);
}
}
static void bt_av_play_pos_changed(void)
{
if (esp_avrc_rn_evt_bit_mask_operation(ESP_AVRC_BIT_MASK_OP_TEST, &s_avrc_peer_rn_cap,
ESP_AVRC_RN_PLAY_POS_CHANGED)) {
esp_avrc_ct_send_register_notification_cmd(APP_RC_CT_TL_RN_PLAY_POS_CHANGE, ESP_AVRC_RN_PLAY_POS_CHANGED, 10);
}
}
void bt_av_notify_evt_handler(uint8_t event_id, esp_avrc_rn_param_t *event_parameter)
{
switch (event_id) {
case ESP_AVRC_RN_TRACK_CHANGE:
bt_av_new_track();
break;
case ESP_AVRC_RN_PLAY_STATUS_CHANGE:
ESP_LOGI(BT_AV_TAG, "Playback status changed: 0x%x", event_parameter->playback);
bt_av_playback_changed();
break;
case ESP_AVRC_RN_PLAY_POS_CHANGED:
ESP_LOGI(BT_AV_TAG, "Play position changed: %d-ms", event_parameter->play_pos);
bt_av_play_pos_changed();
break;
}
}
static void bt_av_hdl_avrc_evt(uint16_t event, void *p_param)
static void bt_av_hdl_avrc_ct_evt(uint16_t event, void *p_param)
{
ESP_LOGD(BT_AV_TAG, "%s evt %d", __func__, event);
ESP_LOGD(BT_RC_CT_TAG, "%s evt %d", __func__, event);
esp_avrc_ct_cb_param_t *rc = (esp_avrc_ct_cb_param_t *)(p_param);
switch (event) {
case ESP_AVRC_CT_CONNECTION_STATE_EVT: {
uint8_t *bda = rc->conn_stat.remote_bda;
ESP_LOGI(BT_AV_TAG, "AVRC conn_state evt: state %d, [%02x:%02x:%02x:%02x:%02x:%02x]",
ESP_LOGI(BT_RC_CT_TAG, "AVRC conn_state evt: state %d, [%02x:%02x:%02x:%02x:%02x:%02x]",
rc->conn_stat.connected, bda[0], bda[1], bda[2], bda[3], bda[4], bda[5]);
if (rc->conn_stat.connected) {
bt_av_new_track();
// get remote supported event_ids of peer AVRCP Target
esp_avrc_ct_send_get_rn_capabilities_cmd(APP_RC_CT_TL_GET_CAPS);
} else {
// clear peer notification capability record
s_avrc_peer_rn_cap.bits = 0;
}
break;
}
case ESP_AVRC_CT_PASSTHROUGH_RSP_EVT: {
ESP_LOGI(BT_AV_TAG, "AVRC passthrough rsp: key_code 0x%x, key_state %d", rc->psth_rsp.key_code, rc->psth_rsp.key_state);
ESP_LOGI(BT_RC_CT_TAG, "AVRC passthrough rsp: key_code 0x%x, key_state %d", rc->psth_rsp.key_code, rc->psth_rsp.key_state);
break;
}
case ESP_AVRC_CT_METADATA_RSP_EVT: {
ESP_LOGI(BT_AV_TAG, "AVRC metadata rsp: attribute id 0x%x, %s", rc->meta_rsp.attr_id, rc->meta_rsp.attr_text);
ESP_LOGI(BT_RC_CT_TAG, "AVRC metadata rsp: attribute id 0x%x, %s", rc->meta_rsp.attr_id, rc->meta_rsp.attr_text);
free(rc->meta_rsp.attr_text);
break;
}
case ESP_AVRC_CT_CHANGE_NOTIFY_EVT: {
ESP_LOGI(BT_AV_TAG, "AVRC event notification: %d, param: %d", rc->change_ntf.event_id, rc->change_ntf.event_parameter);
bt_av_notify_evt_handler(rc->change_ntf.event_id, rc->change_ntf.event_parameter);
ESP_LOGI(BT_RC_CT_TAG, "AVRC event notification: %d", rc->change_ntf.event_id);
bt_av_notify_evt_handler(rc->change_ntf.event_id, &rc->change_ntf.event_parameter);
break;
}
case ESP_AVRC_CT_REMOTE_FEATURES_EVT: {
ESP_LOGI(BT_AV_TAG, "AVRC remote features 0x%x", rc->rmt_feats.feat_mask);
ESP_LOGI(BT_RC_CT_TAG, "AVRC remote features %x, TG features %x", rc->rmt_feats.feat_mask, rc->rmt_feats.tg_feat_flag);
break;
}
case ESP_AVRC_CT_GET_RN_CAPABILITIES_RSP_EVT: {
ESP_LOGI(BT_RC_CT_TAG, "remote rn_cap: count %d, bitmask 0x%x", rc->get_rn_caps_rsp.cap_count,
rc->get_rn_caps_rsp.evt_set.bits);
s_avrc_peer_rn_cap.bits = rc->get_rn_caps_rsp.evt_set.bits;
bt_av_new_track();
bt_av_playback_changed();
bt_av_play_pos_changed();
break;
}
default:
ESP_LOGE(BT_AV_TAG, "%s unhandled evt %d", __func__, event);
ESP_LOGE(BT_RC_CT_TAG, "%s unhandled evt %d", __func__, event);
break;
}
}
static void volume_set_by_controller(uint8_t volume)
{
ESP_LOGI(BT_RC_TG_TAG, "Volume is set by remote controller %d%%\n", (uint32_t)volume * 100 / 0x7f);
_lock_acquire(&s_volume_lock);
s_volume = volume;
_lock_release(&s_volume_lock);
}
static void volume_set_by_local_host(uint8_t volume)
{
ESP_LOGI(BT_RC_TG_TAG, "Volume is set locally to: %d%%", (uint32_t)volume * 100 / 0x7f);
_lock_acquire(&s_volume_lock);
s_volume = volume;
_lock_release(&s_volume_lock);
if (s_volume_notify) {
esp_avrc_rn_param_t rn_param;
rn_param.volume = s_volume;
esp_avrc_tg_send_rn_rsp(ESP_AVRC_RN_VOLUME_CHANGE, ESP_AVRC_RN_RSP_CHANGED, &rn_param);
s_volume_notify = false;
}
}
static void volume_change_simulation(void *arg)
{
ESP_LOGI(BT_RC_TG_TAG, "start volume change simulation");
for (;;) {
vTaskDelay(10000 / portTICK_RATE_MS);
uint8_t volume = (s_volume + 5) & 0x7f;
volume_set_by_local_host(volume);
}
}
static void bt_av_hdl_avrc_tg_evt(uint16_t event, void *p_param)
{
ESP_LOGD(BT_RC_TG_TAG, "%s evt %d", __func__, event);
esp_avrc_tg_cb_param_t *rc = (esp_avrc_tg_cb_param_t *)(p_param);
switch (event) {
case ESP_AVRC_TG_CONNECTION_STATE_EVT: {
uint8_t *bda = rc->conn_stat.remote_bda;
ESP_LOGI(BT_RC_TG_TAG, "AVRC conn_state evt: state %d, [%02x:%02x:%02x:%02x:%02x:%02x]",
rc->conn_stat.connected, bda[0], bda[1], bda[2], bda[3], bda[4], bda[5]);
if (rc->conn_stat.connected) {
// create task to simulate volume change
xTaskCreate(volume_change_simulation, "vcsT", 2048, NULL, 5, &s_vcs_task_hdl);
} else {
vTaskDelete(s_vcs_task_hdl);
ESP_LOGI(BT_RC_TG_TAG, "Stop volume change simulation");
}
break;
}
case ESP_AVRC_TG_PASSTHROUGH_CMD_EVT: {
ESP_LOGI(BT_RC_TG_TAG, "AVRC passthrough cmd: key_code 0x%x, key_state %d", rc->psth_cmd.key_code, rc->psth_cmd.key_state);
break;
}
case ESP_AVRC_TG_SET_ABSOLUTE_VOLUME_CMD_EVT: {
ESP_LOGI(BT_RC_TG_TAG, "AVRC set absolute volume: %d%%", (int)rc->set_abs_vol.volume * 100/ 0x7f);
volume_set_by_controller(rc->set_abs_vol.volume);
break;
}
case ESP_AVRC_TG_REGISTER_NOTIFICATION_EVT: {
ESP_LOGI(BT_RC_TG_TAG, "AVRC register event notification: %d, param: 0x%x", rc->reg_ntf.event_id, rc->reg_ntf.event_parameter);
if (rc->reg_ntf.event_id == ESP_AVRC_RN_VOLUME_CHANGE) {
s_volume_notify = true;
esp_avrc_rn_param_t rn_param;
rn_param.volume = s_volume;
esp_avrc_tg_send_rn_rsp(ESP_AVRC_RN_VOLUME_CHANGE, ESP_AVRC_RN_RSP_INTERIM, &rn_param);
}
break;
}
case ESP_AVRC_TG_REMOTE_FEATURES_EVT: {
ESP_LOGI(BT_RC_TG_TAG, "AVRC remote features %x, CT features %x", rc->rmt_feats.feat_mask, rc->rmt_feats.ct_feat_flag);
break;
}
default:
ESP_LOGE(BT_RC_TG_TAG, "%s unhandled evt %d", __func__, event);
break;
}
}

View file

@ -14,6 +14,8 @@
#include "esp_avrc_api.h"
#define BT_AV_TAG "BT_AV"
#define BT_RC_TG_TAG "RCTG"
#define BT_RC_CT_TAG "RCCT"
/**
* @brief callback function for A2DP sink
@ -30,4 +32,9 @@ void bt_app_a2d_data_cb(const uint8_t *data, uint32_t len);
*/
void bt_app_rc_ct_cb(esp_avrc_ct_cb_event_t event, esp_avrc_ct_cb_param_t *param);
/**
* @brief callback function for AVRCP target
*/
void bt_app_rc_tg_cb(esp_avrc_tg_cb_event_t event, esp_avrc_tg_cb_param_t *param);
#endif /* __BT_APP_AV_H__*/

View file

@ -21,14 +21,15 @@ static void bt_app_task_handler(void *arg);
static bool bt_app_send_msg(bt_app_msg_t *msg);
static void bt_app_work_dispatched(bt_app_msg_t *msg);
static xQueueHandle bt_app_task_queue = NULL;
static xTaskHandle bt_app_task_handle = NULL;
static xQueueHandle s_bt_app_task_queue = NULL;
static xTaskHandle s_bt_app_task_handle = NULL;
bool bt_app_work_dispatch(bt_app_cb_t p_cback, uint16_t event, void *p_params, int param_len, bt_app_copy_cb_t p_copy_cback)
{
ESP_LOGD(BT_APP_CORE_TAG, "%s event 0x%x, param len %d", __func__, event, param_len);
bt_app_msg_t msg = { };
bt_app_msg_t msg;
memset(&msg, 0, sizeof(bt_app_msg_t));
msg.sig = BT_APP_SIG_WORK_DISPATCH;
msg.event = event;
@ -56,7 +57,7 @@ static bool bt_app_send_msg(bt_app_msg_t *msg)
return false;
}
if (xQueueSend(bt_app_task_queue, msg, 10 / portTICK_RATE_MS) != pdTRUE) {
if (xQueueSend(s_bt_app_task_queue, msg, 10 / portTICK_RATE_MS) != pdTRUE) {
ESP_LOGE(BT_APP_CORE_TAG, "%s xQueue send failed", __func__);
return false;
}
@ -74,14 +75,14 @@ static void bt_app_task_handler(void *arg)
{
bt_app_msg_t msg;
for (;;) {
if (pdTRUE == xQueueReceive(bt_app_task_queue, &msg, (portTickType)portMAX_DELAY)) {
if (pdTRUE == xQueueReceive(s_bt_app_task_queue, &msg, (portTickType)portMAX_DELAY)) {
ESP_LOGD(BT_APP_CORE_TAG, "%s, sig 0x%x, 0x%x", __func__, msg.sig, msg.event);
switch (msg.sig) {
case BT_APP_SIG_WORK_DISPATCH:
bt_app_work_dispatched(&msg);
break;
default:
ESP_LOGW(BT_APP_CORE_TAG, "%s, unhandled sig: 0x%x", __func__, msg.sig);
ESP_LOGW(BT_APP_CORE_TAG, "%s, unhandled sig: %d", __func__, msg.sig);
break;
} // switch (msg.sig)
@ -94,19 +95,19 @@ static void bt_app_task_handler(void *arg)
void bt_app_task_start_up(void)
{
bt_app_task_queue = xQueueCreate(10, sizeof(bt_app_msg_t));
xTaskCreate(bt_app_task_handler, "BtAppT", 2048, NULL, configMAX_PRIORITIES - 3, bt_app_task_handle);
s_bt_app_task_queue = xQueueCreate(10, sizeof(bt_app_msg_t));
xTaskCreate(bt_app_task_handler, "BtAppT", 3072, NULL, configMAX_PRIORITIES - 3, &s_bt_app_task_handle);
return;
}
void bt_app_task_shut_down(void)
{
if (bt_app_task_handle) {
vTaskDelete(bt_app_task_handle);
bt_app_task_handle = NULL;
if (s_bt_app_task_handle) {
vTaskDelete(s_bt_app_task_handle);
s_bt_app_task_handle = NULL;
}
if (bt_app_task_queue) {
vQueueDelete(bt_app_task_queue);
bt_app_task_queue = NULL;
if (s_bt_app_task_queue) {
vQueueDelete(s_bt_app_task_queue);
s_bt_app_task_queue = NULL;
}
}

View file

@ -599,6 +599,39 @@ static void ble_gatts_init(void)
ESP_LOGE(BT_BLE_COEX_TAG, "set local MTU failed, error code = 0x%x", local_mtu_ret);
}
}
void bt_app_gap_cb(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *param)
{
switch (event) {
case ESP_BT_GAP_AUTH_CMPL_EVT: {
if (param->auth_cmpl.stat == ESP_BT_STATUS_SUCCESS) {
ESP_LOGI(BT_BLE_COEX_TAG, "authentication success: %s", param->auth_cmpl.device_name);
esp_log_buffer_hex(BT_BLE_COEX_TAG, param->auth_cmpl.bda, ESP_BD_ADDR_LEN);
} else {
ESP_LOGE(BT_BLE_COEX_TAG, "authentication failed, status:%d", param->auth_cmpl.stat);
}
break;
}
#if (CONFIG_BT_SSP_ENABLED == true)
case ESP_BT_GAP_CFM_REQ_EVT:
ESP_LOGI(BT_BLE_COEX_TAG, "ESP_BT_GAP_CFM_REQ_EVT Please compare the numeric value: %d", param->cfm_req.num_val);
esp_bt_gap_ssp_confirm_reply(param->cfm_req.bda, true);
break;
case ESP_BT_GAP_KEY_NOTIF_EVT:
ESP_LOGI(BT_BLE_COEX_TAG, "ESP_BT_GAP_KEY_NOTIF_EVT passkey:%d", param->key_notif.passkey);
break;
case ESP_BT_GAP_KEY_REQ_EVT:
ESP_LOGI(BT_BLE_COEX_TAG, "ESP_BT_GAP_KEY_REQ_EVT Please enter passkey!");
break;
#endif
default: {
ESP_LOGI(BT_BLE_COEX_TAG, "event: %d", event);
break;
}
}
return;
}
/* handler for bluetooth stack enabled events */
static void bt_av_hdl_stack_evt(uint16_t event, void *p_param)
{
@ -608,14 +641,23 @@ static void bt_av_hdl_stack_evt(uint16_t event, void *p_param)
/* set up bt device name */
esp_bt_dev_set_device_name(BT_DEVICE_NAME);
/* initialize A2DP sink */
esp_a2d_register_callback(&bt_app_a2d_cb);
esp_a2d_sink_register_data_callback(bt_app_a2d_data_cb);
esp_a2d_sink_init();
esp_bt_gap_register_callback(bt_app_gap_cb);
/* initialize AVRCP controller */
esp_avrc_ct_init();
esp_avrc_ct_register_callback(bt_app_rc_ct_cb);
/* initialize AVRCP target */
assert (esp_avrc_tg_init() == ESP_OK);
esp_avrc_tg_register_callback(bt_app_rc_tg_cb);
esp_avrc_rn_evt_cap_mask_t evt_set = {0};
esp_avrc_rn_evt_bit_mask_operation(ESP_AVRC_BIT_MASK_OP_SET, &evt_set, ESP_AVRC_RN_VOLUME_CHANGE);
assert(esp_avrc_tg_set_rn_evt_cap(&evt_set) == ESP_OK);
/* initialize A2DP sink */
esp_a2d_register_callback(&bt_app_a2d_cb);
esp_a2d_sink_register_data_callback(bt_app_a2d_data_cb);
esp_a2d_sink_init();
/* set discoverable and connectable mode, wait to be connected */
esp_bt_gap_set_scan_mode(ESP_BT_CONNECTABLE, ESP_BT_GENERAL_DISCOVERABLE);
@ -629,26 +671,24 @@ static void bt_av_hdl_stack_evt(uint16_t event, void *p_param)
void app_main()
{
esp_err_t ret;
// Initialize NVS.
ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
/* Initialize NVS — it is used to store PHY calibration data */
esp_err_t err = nvs_flash_init();
if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
err = nvs_flash_init();
}
ESP_ERROR_CHECK( ret );
ESP_ERROR_CHECK(err);
i2s_config_t i2s_config = {
#ifdef CONFIG_A2DP_SINK_OUTPUT_INTERNAL_DAC
.mode = I2S_MODE_DAC_BUILT_IN,
.mode = I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_DAC_BUILT_IN,
#else
.mode = I2S_MODE_MASTER | I2S_MODE_TX, // Only TX
#endif
.sample_rate = 44100,
.bits_per_sample = 16,
.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, //2-channels
.communication_format = I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_MSB,
.communication_format = I2S_COMM_FORMAT_I2S_MSB,
.dma_buf_count = 6,
.dma_buf_len = 60,
.intr_alloc_flags = 0, //Default interrupt priority
@ -658,6 +698,7 @@ void app_main()
i2s_driver_install(0, &i2s_config, 0, NULL);
#ifdef CONFIG_A2DP_SINK_OUTPUT_INTERNAL_DAC
i2s_set_dac_mode(I2S_DAC_CHANNEL_BOTH_EN);
i2s_set_pin(0, NULL);
#else
i2s_pin_config_t pin_config = {
@ -671,34 +712,51 @@ void app_main()
#endif
esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
ret = esp_bt_controller_init(&bt_cfg);
if (ret) {
ESP_LOGE(BT_BLE_COEX_TAG, "%s initialize controller failed\n", __func__);
if ((err = esp_bt_controller_init(&bt_cfg)) != ESP_OK) {
ESP_LOGE(BT_BLE_COEX_TAG, "%s initialize controller failed: %s\n", __func__, esp_err_to_name(err));
return;
}
ret = esp_bt_controller_enable(ESP_BT_MODE_BTDM);
if (ret) {
ESP_LOGE(BT_BLE_COEX_TAG, "%s enable controller failed\n", __func__);
if ((err = esp_bt_controller_enable(ESP_BT_MODE_BTDM)) != ESP_OK) {
ESP_LOGE(BT_BLE_COEX_TAG, "%s enable controller failed: %s\n", __func__, esp_err_to_name(err));
return;
}
ret = esp_bluedroid_init();
if (ret) {
ESP_LOGE(BT_BLE_COEX_TAG, "%s init bluetooth failed\n", __func__);
if ((err = esp_bluedroid_init()) != ESP_OK) {
ESP_LOGE(BT_BLE_COEX_TAG, "%s initialize bluedroid failed: %s\n", __func__, esp_err_to_name(err));
return;
}
ret = esp_bluedroid_enable();
if (ret) {
ESP_LOGE(BT_BLE_COEX_TAG, "%s enable bluetooth failed\n", __func__);
if ((err = esp_bluedroid_enable()) != ESP_OK) {
ESP_LOGE(BT_BLE_COEX_TAG, "%s enable bluedroid failed: %s\n", __func__, esp_err_to_name(err));
return;
}
/* create application task */
bt_app_task_start_up();
/* Bluetooth device name, connection mode and profile set up */
bt_app_work_dispatch(bt_av_hdl_stack_evt, BT_APP_EVT_STACK_UP, NULL, 0, NULL);
#if (CONFIG_BT_SSP_ENABLED == true)
/* Set default parameters for Secure Simple Pairing */
esp_bt_sp_param_t param_type = ESP_BT_SP_IOCAP_MODE;
esp_bt_io_cap_t iocap = ESP_BT_IO_CAP_IO;
esp_bt_gap_set_security_param(param_type, &iocap, sizeof(uint8_t));
#endif
/*
* Set default parameters for Legacy Pairing
* Use fixed pin code
*/
esp_bt_pin_type_t pin_type = ESP_BT_PIN_TYPE_FIXED;
esp_bt_pin_code_t pin_code;
pin_code[0] = '1';
pin_code[1] = '2';
pin_code[2] = '3';
pin_code[3] = '4';
esp_bt_gap_set_pin(pin_type, 4, pin_code);
//gatt server init
ble_gatts_init();
}

View file

@ -25,15 +25,31 @@
#include "freertos/task.h"
#include "driver/i2s.h"
#include "sys/lock.h"
// AVRCP used transaction label
#define APP_RC_CT_TL_GET_CAPS (0)
#define APP_RC_CT_TL_GET_META_DATA (1)
#define APP_RC_CT_TL_RN_TRACK_CHANGE (2)
#define APP_RC_CT_TL_RN_PLAYBACK_CHANGE (3)
#define APP_RC_CT_TL_RN_PLAY_POS_CHANGE (4)
/* a2dp event handler */
static void bt_av_hdl_a2d_evt(uint16_t event, void *p_param);
/* avrc event handler */
static void bt_av_hdl_avrc_evt(uint16_t event, void *p_param);
/* avrc CT event handler */
static void bt_av_hdl_avrc_ct_evt(uint16_t event, void *p_param);
/* avrc TG event handler */
static void bt_av_hdl_avrc_tg_evt(uint16_t event, void *p_param);
static uint32_t s_pkt_cnt = 0;
static esp_a2d_audio_state_t s_audio_state = ESP_A2D_AUDIO_STATE_STOPPED;
static const char *s_a2d_conn_state_str[] = {"Disconnected", "Connecting", "Connected", "Disconnecting"};
static const char *s_a2d_audio_state_str[] = {"Suspended", "Stopped", "Started"};
static esp_avrc_rn_evt_cap_mask_t s_avrc_peer_rn_cap;
static _lock_t s_volume_lock;
static xTaskHandle s_vcs_task_hdl = NULL;
static uint8_t s_volume = 0;
static bool s_volume_notify;
/* callback for A2DP sink */
void bt_app_a2d_cb(esp_a2d_cb_event_t event, esp_a2d_cb_param_t *param)
@ -79,12 +95,29 @@ void bt_app_rc_ct_cb(esp_avrc_ct_cb_event_t event, esp_avrc_ct_cb_param_t *param
case ESP_AVRC_CT_CONNECTION_STATE_EVT:
case ESP_AVRC_CT_PASSTHROUGH_RSP_EVT:
case ESP_AVRC_CT_CHANGE_NOTIFY_EVT:
case ESP_AVRC_CT_REMOTE_FEATURES_EVT: {
bt_app_work_dispatch(bt_av_hdl_avrc_evt, event, param, sizeof(esp_avrc_ct_cb_param_t), NULL);
case ESP_AVRC_CT_REMOTE_FEATURES_EVT:
case ESP_AVRC_CT_GET_RN_CAPABILITIES_RSP_EVT: {
bt_app_work_dispatch(bt_av_hdl_avrc_ct_evt, event, param, sizeof(esp_avrc_ct_cb_param_t), NULL);
break;
}
default:
ESP_LOGE(BT_AV_TAG, "Invalid AVRC event: %d", event);
ESP_LOGE(BT_RC_CT_TAG, "Invalid AVRC event: %d", event);
break;
}
}
void bt_app_rc_tg_cb(esp_avrc_tg_cb_event_t event, esp_avrc_tg_cb_param_t *param)
{
switch (event) {
case ESP_AVRC_TG_CONNECTION_STATE_EVT:
case ESP_AVRC_TG_REMOTE_FEATURES_EVT:
case ESP_AVRC_TG_PASSTHROUGH_CMD_EVT:
case ESP_AVRC_TG_SET_ABSOLUTE_VOLUME_CMD_EVT:
case ESP_AVRC_TG_REGISTER_NOTIFICATION_EVT:
bt_app_work_dispatch(bt_av_hdl_avrc_tg_evt, event, param, sizeof(esp_avrc_tg_cb_param_t), NULL);
break;
default:
ESP_LOGE(BT_RC_TG_TAG, "Invalid AVRC event: %d", event);
break;
}
}
@ -146,57 +179,182 @@ static void bt_av_hdl_a2d_evt(uint16_t event, void *p_param)
}
}
static void bt_av_new_track()
static void bt_av_new_track(void)
{
//Register notifications and request metadata
esp_avrc_ct_send_metadata_cmd(0, ESP_AVRC_MD_ATTR_TITLE | ESP_AVRC_MD_ATTR_ARTIST | ESP_AVRC_MD_ATTR_ALBUM | ESP_AVRC_MD_ATTR_GENRE);
esp_avrc_ct_send_register_notification_cmd(1, ESP_AVRC_RN_TRACK_CHANGE, 0);
// request metadata
uint8_t attr_mask = ESP_AVRC_MD_ATTR_TITLE | ESP_AVRC_MD_ATTR_ARTIST | ESP_AVRC_MD_ATTR_ALBUM | ESP_AVRC_MD_ATTR_GENRE;
esp_avrc_ct_send_metadata_cmd(APP_RC_CT_TL_GET_META_DATA, attr_mask);
// register notification if peer support the event_id
if (esp_avrc_rn_evt_bit_mask_operation(ESP_AVRC_BIT_MASK_OP_TEST, &s_avrc_peer_rn_cap,
ESP_AVRC_RN_TRACK_CHANGE)) {
esp_avrc_ct_send_register_notification_cmd(APP_RC_CT_TL_RN_TRACK_CHANGE, ESP_AVRC_RN_TRACK_CHANGE, 0);
}
}
void bt_av_notify_evt_handler(uint8_t event_id, uint32_t event_parameter)
static void bt_av_playback_changed(void)
{
if (esp_avrc_rn_evt_bit_mask_operation(ESP_AVRC_BIT_MASK_OP_TEST, &s_avrc_peer_rn_cap,
ESP_AVRC_RN_PLAY_STATUS_CHANGE)) {
esp_avrc_ct_send_register_notification_cmd(APP_RC_CT_TL_RN_PLAYBACK_CHANGE, ESP_AVRC_RN_PLAY_STATUS_CHANGE, 0);
}
}
static void bt_av_play_pos_changed(void)
{
if (esp_avrc_rn_evt_bit_mask_operation(ESP_AVRC_BIT_MASK_OP_TEST, &s_avrc_peer_rn_cap,
ESP_AVRC_RN_PLAY_POS_CHANGED)) {
esp_avrc_ct_send_register_notification_cmd(APP_RC_CT_TL_RN_PLAY_POS_CHANGE, ESP_AVRC_RN_PLAY_POS_CHANGED, 10);
}
}
void bt_av_notify_evt_handler(uint8_t event_id, esp_avrc_rn_param_t *event_parameter)
{
switch (event_id) {
case ESP_AVRC_RN_TRACK_CHANGE:
bt_av_new_track();
break;
case ESP_AVRC_RN_PLAY_STATUS_CHANGE:
ESP_LOGI(BT_AV_TAG, "Playback status changed: 0x%x", event_parameter->playback);
bt_av_playback_changed();
break;
case ESP_AVRC_RN_PLAY_POS_CHANGED:
ESP_LOGI(BT_AV_TAG, "Play position changed: %d-ms", event_parameter->play_pos);
bt_av_play_pos_changed();
break;
}
}
static void bt_av_hdl_avrc_evt(uint16_t event, void *p_param)
static void bt_av_hdl_avrc_ct_evt(uint16_t event, void *p_param)
{
ESP_LOGD(BT_AV_TAG, "%s evt %d", __func__, event);
ESP_LOGD(BT_RC_CT_TAG, "%s evt %d", __func__, event);
esp_avrc_ct_cb_param_t *rc = (esp_avrc_ct_cb_param_t *)(p_param);
switch (event) {
case ESP_AVRC_CT_CONNECTION_STATE_EVT: {
uint8_t *bda = rc->conn_stat.remote_bda;
ESP_LOGI(BT_AV_TAG, "AVRC conn_state evt: state %d, [%02x:%02x:%02x:%02x:%02x:%02x]",
ESP_LOGI(BT_RC_CT_TAG, "AVRC conn_state evt: state %d, [%02x:%02x:%02x:%02x:%02x:%02x]",
rc->conn_stat.connected, bda[0], bda[1], bda[2], bda[3], bda[4], bda[5]);
if (rc->conn_stat.connected) {
bt_av_new_track();
// get remote supported event_ids of peer AVRCP Target
esp_avrc_ct_send_get_rn_capabilities_cmd(APP_RC_CT_TL_GET_CAPS);
} else {
// clear peer notification capability record
s_avrc_peer_rn_cap.bits = 0;
}
break;
}
case ESP_AVRC_CT_PASSTHROUGH_RSP_EVT: {
ESP_LOGI(BT_AV_TAG, "AVRC passthrough rsp: key_code 0x%x, key_state %d", rc->psth_rsp.key_code, rc->psth_rsp.key_state);
ESP_LOGI(BT_RC_CT_TAG, "AVRC passthrough rsp: key_code 0x%x, key_state %d", rc->psth_rsp.key_code, rc->psth_rsp.key_state);
break;
}
case ESP_AVRC_CT_METADATA_RSP_EVT: {
ESP_LOGI(BT_AV_TAG, "AVRC metadata rsp: attribute id 0x%x, %s", rc->meta_rsp.attr_id, rc->meta_rsp.attr_text);
ESP_LOGI(BT_RC_CT_TAG, "AVRC metadata rsp: attribute id 0x%x, %s", rc->meta_rsp.attr_id, rc->meta_rsp.attr_text);
free(rc->meta_rsp.attr_text);
break;
}
case ESP_AVRC_CT_CHANGE_NOTIFY_EVT: {
ESP_LOGI(BT_AV_TAG, "AVRC event notification: %d, param: %d", rc->change_ntf.event_id, rc->change_ntf.event_parameter);
bt_av_notify_evt_handler(rc->change_ntf.event_id, rc->change_ntf.event_parameter);
ESP_LOGI(BT_RC_CT_TAG, "AVRC event notification: %d", rc->change_ntf.event_id);
bt_av_notify_evt_handler(rc->change_ntf.event_id, &rc->change_ntf.event_parameter);
break;
}
case ESP_AVRC_CT_REMOTE_FEATURES_EVT: {
ESP_LOGI(BT_AV_TAG, "AVRC remote features %x", rc->rmt_feats.feat_mask);
ESP_LOGI(BT_RC_CT_TAG, "AVRC remote features %x, TG features %x", rc->rmt_feats.feat_mask, rc->rmt_feats.tg_feat_flag);
break;
}
case ESP_AVRC_CT_GET_RN_CAPABILITIES_RSP_EVT: {
ESP_LOGI(BT_RC_CT_TAG, "remote rn_cap: count %d, bitmask 0x%x", rc->get_rn_caps_rsp.cap_count,
rc->get_rn_caps_rsp.evt_set.bits);
s_avrc_peer_rn_cap.bits = rc->get_rn_caps_rsp.evt_set.bits;
bt_av_new_track();
bt_av_playback_changed();
bt_av_play_pos_changed();
break;
}
default:
ESP_LOGE(BT_AV_TAG, "%s unhandled evt %d", __func__, event);
ESP_LOGE(BT_RC_CT_TAG, "%s unhandled evt %d", __func__, event);
break;
}
}
static void volume_set_by_controller(uint8_t volume)
{
ESP_LOGI(BT_RC_TG_TAG, "Volume is set by remote controller %d%%\n", (uint32_t)volume * 100 / 0x7f);
_lock_acquire(&s_volume_lock);
s_volume = volume;
_lock_release(&s_volume_lock);
}
static void volume_set_by_local_host(uint8_t volume)
{
ESP_LOGI(BT_RC_TG_TAG, "Volume is set locally to: %d%%", (uint32_t)volume * 100 / 0x7f);
_lock_acquire(&s_volume_lock);
s_volume = volume;
_lock_release(&s_volume_lock);
if (s_volume_notify) {
esp_avrc_rn_param_t rn_param;
rn_param.volume = s_volume;
esp_avrc_tg_send_rn_rsp(ESP_AVRC_RN_VOLUME_CHANGE, ESP_AVRC_RN_RSP_CHANGED, &rn_param);
s_volume_notify = false;
}
}
static void volume_change_simulation(void *arg)
{
ESP_LOGI(BT_RC_TG_TAG, "start volume change simulation");
for (;;) {
vTaskDelay(10000 / portTICK_RATE_MS);
uint8_t volume = (s_volume + 5) & 0x7f;
volume_set_by_local_host(volume);
}
}
static void bt_av_hdl_avrc_tg_evt(uint16_t event, void *p_param)
{
ESP_LOGD(BT_RC_TG_TAG, "%s evt %d", __func__, event);
esp_avrc_tg_cb_param_t *rc = (esp_avrc_tg_cb_param_t *)(p_param);
switch (event) {
case ESP_AVRC_TG_CONNECTION_STATE_EVT: {
uint8_t *bda = rc->conn_stat.remote_bda;
ESP_LOGI(BT_RC_TG_TAG, "AVRC conn_state evt: state %d, [%02x:%02x:%02x:%02x:%02x:%02x]",
rc->conn_stat.connected, bda[0], bda[1], bda[2], bda[3], bda[4], bda[5]);
if (rc->conn_stat.connected) {
// create task to simulate volume change
xTaskCreate(volume_change_simulation, "vcsT", 2048, NULL, 5, &s_vcs_task_hdl);
} else {
vTaskDelete(s_vcs_task_hdl);
ESP_LOGI(BT_RC_TG_TAG, "Stop volume change simulation");
}
break;
}
case ESP_AVRC_TG_PASSTHROUGH_CMD_EVT: {
ESP_LOGI(BT_RC_TG_TAG, "AVRC passthrough cmd: key_code 0x%x, key_state %d", rc->psth_cmd.key_code, rc->psth_cmd.key_state);
break;
}
case ESP_AVRC_TG_SET_ABSOLUTE_VOLUME_CMD_EVT: {
ESP_LOGI(BT_RC_TG_TAG, "AVRC set absolute volume: %d%%", (int)rc->set_abs_vol.volume * 100/ 0x7f);
volume_set_by_controller(rc->set_abs_vol.volume);
break;
}
case ESP_AVRC_TG_REGISTER_NOTIFICATION_EVT: {
ESP_LOGI(BT_RC_TG_TAG, "AVRC register event notification: %d, param: 0x%x", rc->reg_ntf.event_id, rc->reg_ntf.event_parameter);
if (rc->reg_ntf.event_id == ESP_AVRC_RN_VOLUME_CHANGE) {
s_volume_notify = true;
esp_avrc_rn_param_t rn_param;
rn_param.volume = s_volume;
esp_avrc_tg_send_rn_rsp(ESP_AVRC_RN_VOLUME_CHANGE, ESP_AVRC_RN_RSP_INTERIM, &rn_param);
}
break;
}
case ESP_AVRC_TG_REMOTE_FEATURES_EVT: {
ESP_LOGI(BT_RC_TG_TAG, "AVRC remote features %x, CT features %x", rc->rmt_feats.feat_mask, rc->rmt_feats.ct_feat_flag);
break;
}
default:
ESP_LOGE(BT_RC_TG_TAG, "%s unhandled evt %d", __func__, event);
break;
}
}

View file

@ -14,6 +14,8 @@
#include "esp_avrc_api.h"
#define BT_AV_TAG "BT_AV"
#define BT_RC_TG_TAG "RCTG"
#define BT_RC_CT_TAG "RCCT"
/**
* @brief callback function for A2DP sink
@ -30,4 +32,9 @@ void bt_app_a2d_data_cb(const uint8_t *data, uint32_t len);
*/
void bt_app_rc_ct_cb(esp_avrc_ct_cb_event_t event, esp_avrc_ct_cb_param_t *param);
/**
* @brief callback function for AVRCP target
*/
void bt_app_rc_tg_cb(esp_avrc_tg_cb_event_t event, esp_avrc_tg_cb_param_t *param);
#endif /* __BT_APP_AV_H__*/

View file

@ -96,7 +96,7 @@ static void bt_app_task_handler(void *arg)
void bt_app_task_start_up(void)
{
s_bt_app_task_queue = xQueueCreate(10, sizeof(bt_app_msg_t));
xTaskCreate(bt_app_task_handler, "BtAppT", 2048, NULL, configMAX_PRIORITIES - 3, &s_bt_app_task_handle);
xTaskCreate(bt_app_task_handler, "BtAppT", 3072, NULL, configMAX_PRIORITIES - 3, &s_bt_app_task_handle);
return;
}

View file

@ -132,6 +132,7 @@ void app_main()
pin_code[2] = '3';
pin_code[3] = '4';
esp_bt_gap_set_pin(pin_type, 4, pin_code);
}
void bt_app_gap_cb(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *param)
@ -177,14 +178,22 @@ static void bt_av_hdl_stack_evt(uint16_t event, void *p_param)
esp_bt_dev_set_device_name(dev_name);
esp_bt_gap_register_callback(bt_app_gap_cb);
/* initialize A2DP sink */
esp_a2d_register_callback(&bt_app_a2d_cb);
esp_a2d_sink_register_data_callback(bt_app_a2d_data_cb);
esp_a2d_sink_init();
/* initialize AVRCP controller */
esp_avrc_ct_init();
esp_avrc_ct_register_callback(bt_app_rc_ct_cb);
/* initialize AVRCP target */
assert (esp_avrc_tg_init() == ESP_OK);
esp_avrc_tg_register_callback(bt_app_rc_tg_cb);
esp_avrc_rn_evt_cap_mask_t evt_set = {0};
esp_avrc_rn_evt_bit_mask_operation(ESP_AVRC_BIT_MASK_OP_SET, &evt_set, ESP_AVRC_RN_VOLUME_CHANGE);
assert(esp_avrc_tg_set_rn_evt_cap(&evt_set) == ESP_OK);
/* initialize A2DP sink */
esp_a2d_register_callback(&bt_app_a2d_cb);
esp_a2d_sink_register_data_callback(bt_app_a2d_data_cb);
esp_a2d_sink_init();
/* set discoverable and connectable mode, wait to be connected */
esp_bt_gap_set_scan_mode(ESP_BT_CONNECTABLE, ESP_BT_GENERAL_DISCOVERABLE);