component/bt: implement AVRCP Target APIs

1. Add more notification events to the enum according to the event list in AVRCP specification.
2. Add API and callback events for basic AVRCP target functionalities to do init, deinit, callback-registration, connection status indication.
3. Implement API to set/get supported PASSTHROUGH command on local AVRCP TG, implement callback events for remote passthrough command indication.
4. Implement API to set/get supported notification eventIDs on local AVRCP TG, implement API to send event notifications to remote CT. \
   Currently supported event in TG only includes ESP_AVRC_RN_VOLUME_CHANGE(0xd), which can be extended in later commits.
5. Implement callback events for SetAbsoluteVolume command indication on TG.
6. Add limitation of event_ids supported in RegisterNotification command in CT. The supported event_ids include: \
   ESP_AVRC_RN_PLAY_STATUS_CHANGE(0x1), ESP_AVRC_RN_TRACK_CHANGE(0x2), ESP_AVRC_RN_PLAY_POS_CHANGE(0x5), ESP_AVRC_RN_VOLUME_CHANGE(0xd).
7. Add feature bit mask in parameter of callback event ESP_AVRC_CT_REMOTE_FEATURES_EVT for peer feature information got from SDP.
8. Add API and callback event to AVRCP CT to retrieve remote TG's supported notification event capabilities.
9. Modify data type for parameter of callback event ESP_AVRC_CT_CHANGE_NOTIFY_EVT.
10. Change AVRCP version from 1.3 to 1.4 for compatibility cause in using AbsoluteVolume feature.
11. Modify local AVRCP device to be category 1 as CT and category 2 as TG that applies to bluetooth headphones or speakers.
12. Update the use of AVRCP APIs and events in the two examples: a2dp_sink and a2dp_gatts_coex, which include the demo of volume control and notification.
This commit is contained in:
wangmengyang 2018-12-21 17:04:21 +08:00 committed by baohongde
parent a9425cd045
commit 61bd453c15
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_SCAN_MODE_CONNECTABLE_DISCOVERABLE);
} else if (a2d->conn_stat.state == ESP_A2D_CONNECTION_STATE_CONNECTED){
esp_bt_gap_set_scan_mode(ESP_BT_SCAN_MODE_NONE);
}
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);