Merge branch 'feature/btdm_avrcp_volume' into 'master'

components/bt: Add AVRCP feature about volume

See merge request idf/esp-idf!5209
This commit is contained in:
Jiang Jiang Jian 2019-06-30 16:40:10 +08:00
commit 70474ae844
8 changed files with 315 additions and 64 deletions

View file

@ -29,7 +29,7 @@ esp_err_t esp_avrc_ct_register_callback(esp_avrc_ct_cb_t callback)
}
if (callback == NULL) {
return ESP_FAIL;
return ESP_ERR_INVALID_ARG;
}
btc_profile_cb_set(BTC_PID_AVRC_CT, callback);
@ -76,8 +76,8 @@ esp_err_t esp_avrc_ct_send_set_player_value_cmd(uint8_t tl, uint8_t attr_id, uin
return ESP_ERR_INVALID_STATE;
}
if (tl >= 16 || attr_id > ESP_AVRC_PS_MAX_ATTR - 1) {
return ESP_FAIL;
if (tl > ESP_AVRC_TRANS_LABEL_MAX || attr_id > ESP_AVRC_PS_MAX_ATTR - 1) {
return ESP_ERR_INVALID_ARG;
}
btc_msg_t msg;
@ -103,7 +103,7 @@ esp_err_t esp_avrc_ct_send_get_rn_capabilities_cmd(uint8_t tl)
return ESP_ERR_INVALID_STATE;
}
if (tl >= 16) {
if (tl > ESP_AVRC_TRANS_LABEL_MAX) {
return ESP_ERR_INVALID_ARG;
}
@ -128,8 +128,8 @@ esp_err_t esp_avrc_ct_send_register_notification_cmd(uint8_t tl, uint8_t event_i
return ESP_ERR_INVALID_STATE;
}
if (tl >= 16 || event_id > ESP_AVRC_RN_MAX_EVT - 1) {
return ESP_FAIL;
if (tl > ESP_AVRC_TRANS_LABEL_MAX || event_id > ESP_AVRC_RN_MAX_EVT - 1) {
return ESP_ERR_INVALID_ARG;
}
if (!btc_avrc_ct_rn_evt_supported(event_id)) {
@ -153,14 +153,48 @@ esp_err_t esp_avrc_ct_send_register_notification_cmd(uint8_t tl, uint8_t event_i
return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL;
}
esp_err_t esp_avrc_ct_send_set_absolute_volume_cmd(uint8_t tl, uint8_t volume)
{
if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) {
return ESP_ERR_INVALID_STATE;
}
if (tl > ESP_AVRC_TRANS_LABEL_MAX) {
return ESP_ERR_INVALID_ARG;
}
if (volume > BTC_AVRC_MAX_VOLUME) {
return ESP_ERR_INVALID_ARG;
}
if (!btc_avrc_ct_rn_evt_supported(ESP_AVRC_RN_VOLUME_CHANGE)) {
return ESP_ERR_NOT_SUPPORTED;
}
btc_msg_t msg;
msg.sig = BTC_SIG_API_CALL;
msg.pid = BTC_PID_AVRC_CT;
msg.act = BTC_AVRC_CTRL_API_SND_SET_ABSOLUTE_VOLUME_EVT;
btc_avrc_args_t arg;
memset(&arg, 0, sizeof(btc_avrc_args_t));
arg.set_abs_vol_cmd.tl = tl;
arg.set_abs_vol_cmd.volume = volume;
/* 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_metadata_cmd(uint8_t tl, uint8_t attr_mask)
{
if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) {
return ESP_ERR_INVALID_STATE;
}
if (tl >= 16) {
return ESP_FAIL;
if (tl > ESP_AVRC_TRANS_LABEL_MAX) {
return ESP_ERR_INVALID_ARG;
}
btc_msg_t msg;
@ -185,8 +219,8 @@ esp_err_t esp_avrc_ct_send_passthrough_cmd(uint8_t tl, uint8_t key_code, uint8_t
return ESP_ERR_INVALID_STATE;
}
if (tl >= 16 || key_state > ESP_AVRC_PT_CMD_STATE_RELEASED) {
return ESP_FAIL;
if (tl > ESP_AVRC_TRANS_LABEL_MAX || key_state > ESP_AVRC_PT_CMD_STATE_RELEASED) {
return ESP_ERR_INVALID_ARG;
}
btc_msg_t msg;
@ -217,7 +251,7 @@ esp_err_t esp_avrc_tg_register_callback(esp_avrc_tg_cb_t callback)
}
if (callback == NULL) {
return ESP_FAIL;
return ESP_ERR_INVALID_ARG;
}
btc_profile_cb_set(BTC_PID_AVRC_TG, callback);
@ -445,7 +479,7 @@ esp_err_t esp_avrc_tg_send_rn_rsp(esp_avrc_rn_event_ids_t event_id, esp_avrc_rn_
/* 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

@ -24,6 +24,8 @@
extern "C" {
#endif
#define ESP_AVRC_TRANS_LABEL_MAX 15 /*!< max transaction label */
/// AVRC feature bit mask
typedef enum {
ESP_AVRC_FEAT_RCTG = 0x0001, /*!< remote control target */
@ -140,6 +142,7 @@ typedef enum {
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_SET_ABSOLUTE_VOLUME_RSP_EVT = 7, /*!< set absolute volume response event */
} esp_avrc_ct_cb_event_t;
/// AVRC Target callback events
@ -327,6 +330,13 @@ typedef union {
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 */
/**
* @brief ESP_AVRC_CT_SET_ABSOLUTE_VOLUME_RSP_EVT
*/
struct avrc_ct_set_volume_rsp_param {
uint8_t volume; /*!< the volume which has actually been set, range is 0 to 0x7f, means 0% to 100% */
} set_volume_rsp; /*!< set absolute volume response event */
} esp_avrc_ct_cb_param_t;
/// AVRC target callback parameters
@ -460,7 +470,8 @@ esp_err_t esp_avrc_ct_send_get_rn_capabilities_cmd(uint8_t tl);
*
* @param[in] tl : transaction label, 0 to 15, consecutive commands should use different values.
* @param[in] event_id : id of events, e.g. ESP_AVRC_RN_PLAY_STATUS_CHANGE, ESP_AVRC_RN_TRACK_CHANGE, etc.
* @param[in] event_parameter : special parameters, eg. playback interval for ESP_AVRC_RN_PLAY_POS_CHANGED
* @param[in] event_parameter : playback interval for ESP_AVRC_RN_PLAY_POS_CHANGED;
* For other events , value of this parameter is ignored.
* @return
* - ESP_OK: success
* - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled
@ -469,6 +480,19 @@ esp_err_t esp_avrc_ct_send_get_rn_capabilities_cmd(uint8_t tl);
*/
esp_err_t esp_avrc_ct_send_register_notification_cmd(uint8_t tl, uint8_t event_id, uint32_t event_parameter);
/**
* @brief Send set absolute volume command to AVRCP target, 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.
* @param[in] volume : volume, 0 to 0x7f, means 0% to 100%
* @return
* - ESP_OK: success
* - 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_set_absolute_volume_cmd(uint8_t tl, uint8_t volume);
/**
* @brief Send metadata command to AVRCP target, This function should be called after
@ -562,22 +586,22 @@ esp_err_t esp_avrc_tg_get_psth_cmd_filter(esp_avrc_psth_filter_t filter, esp_avr
/**
*
* @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
* @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
* 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()
* 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
* - ESP_ERR_NOT_SUPPORTED:: if filter type is ESP_AVRC_PSTH_FILTER_ALLOWED_CMD, or cmd_set
* includes unallowed commands
*
*/
@ -601,13 +625,13 @@ bool esp_avrc_psth_bit_mask_operation(esp_avrc_bit_mask_op_t op, esp_avrc_psth_b
/**
*
* @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
* 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
* 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
* events selected to be supported under current configuration, The configuration can be
* changed using esp_avrc_tg_set_rn_evt_cap()
*
* @return
@ -652,7 +676,7 @@ bool esp_avrc_rn_evt_bit_mask_operation(esp_avrc_bit_mask_op_t op, esp_avrc_rn_e
* @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()
* 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

View file

@ -40,8 +40,10 @@ const UINT32 bta_av_meta_caps_co_ids[] = {
AVRC_CO_BROADCOM
};
/* AVRCP cupported categories */
#define BTA_AV_RC_SUPF_CT (AVRC_SUPF_CT_CAT1)
/* AVRCP supported categories */
#define BTA_AV_RC_SNK_SUPF_CT (AVRC_SUPF_CT_CAT1)
#define BTA_AV_RC_SRC_SUPF_CT (AVRC_SUPF_CT_CAT2)
/* Added to modify
** 1. flush timeout
@ -62,9 +64,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_CAT2) /* TODO: | AVRC_SUPF_TG_APP_SETTINGS) */
#define BTA_AV_RC_SNK_SUPF_TG (AVRC_SUPF_TG_CAT2) /* TODO: | AVRC_SUPF_TG_APP_SETTINGS) */
#define BTA_AV_RC_SRC_SUPF_TG (AVRC_SUPF_TG_CAT1) /* TODO: | AVRC_SUPF_TG_APP_SETTINGS) */
#else
#define BTA_AV_RC_SUPF_TG (AVRC_SUPF_TG_CAT2)
#define BTA_AV_RC_SNK_SUPF_TG (AVRC_SUPF_TG_CAT2)
#define BTA_AV_RC_SRC_SUPF_TG (AVRC_SUPF_TG_CAT1)
#endif
/* the MTU for the AVRCP browsing channel */
@ -80,8 +84,10 @@ const tBTA_AV_CFG bta_av_cfg = {
48, /* AVRCP MTU at L2CAP for control channel */
#endif
BTA_AV_MAX_RC_BR_MTU, /* AVRCP MTU at L2CAP for browsing channel */
BTA_AV_RC_SUPF_CT, /* AVRCP controller categories */
BTA_AV_RC_SUPF_TG, /* AVRCP target categories */
BTA_AV_RC_SNK_SUPF_CT, /* AVRCP controller categories as SNK */
BTA_AV_RC_SNK_SUPF_TG, /* AVRCP target categories as SNK */
BTA_AV_RC_SRC_SUPF_CT, /* AVRCP controller categories as SRC */
BTA_AV_RC_SRC_SUPF_TG, /* AVRCP target categories as SRC */
672, /* AVDTP signaling channel MTU at L2CAP */
BTA_AV_MAX_A2DP_MTU, /* AVDTP audio transport channel MTU at L2CAP */
bta_av_audio_flush_to, /* AVDTP audio transport channel flush timeout */

View file

@ -576,9 +576,13 @@ static void bta_av_api_register(tBTA_AV_DATA *p_data)
bta_ar_reg_avct(p_bta_av_cfg->avrc_mtu, p_bta_av_cfg->avrc_br_mtu,
(UINT8)(bta_av_cb.sec_mask & (~BTA_SEC_AUTHORIZE)), BTA_ID_AV);
#endif
bta_ar_reg_avrc(UUID_SERVCLASS_AV_REM_CTRL_TARGET, "AV Remote Control Target\n", NULL,
p_bta_av_cfg->avrc_tg_cat, BTA_ID_AV);
if (p_data->api_reg.tsep == AVDT_TSEP_SRC) {
bta_ar_reg_avrc(UUID_SERVCLASS_AV_REM_CTRL_TARGET, "AV Remote Control Target\n", NULL,
p_bta_av_cfg->avrc_src_tg_cat, BTA_ID_AV);
} else {
bta_ar_reg_avrc(UUID_SERVCLASS_AV_REM_CTRL_TARGET, "AV Remote Control Target\n", NULL,
p_bta_av_cfg->avrc_snk_tg_cat, BTA_ID_AV);
}
#endif
}
@ -711,8 +715,13 @@ static void bta_av_api_register(tBTA_AV_DATA *p_data)
}
#if( defined BTA_AR_INCLUDED ) && (BTA_AR_INCLUDED == TRUE)
/* create an SDP record as AVRC CT. */
bta_ar_reg_avrc(UUID_SERVCLASS_AV_REMOTE_CONTROL, NULL, NULL,
p_bta_av_cfg->avrc_ct_cat, BTA_ID_AV);
if (p_data->api_reg.tsep == AVDT_TSEP_SRC) {
bta_ar_reg_avrc(UUID_SERVCLASS_AV_REMOTE_CONTROL, "AV Remote Control Controller\n", NULL,
p_bta_av_cfg->avrc_src_ct_cat, BTA_ID_AV);
} else {
bta_ar_reg_avrc(UUID_SERVCLASS_AV_REMOTE_CONTROL, "AV Remote Control Controller\n", NULL,
p_bta_av_cfg->avrc_snk_ct_cat, BTA_ID_AV);
}
#endif
}
}

View file

@ -529,8 +529,10 @@ typedef struct {
UINT32 company_id; /* AVRCP Company ID */
UINT16 avrc_mtu; /* AVRCP MTU at L2CAP for control channel */
UINT16 avrc_br_mtu; /* AVRCP MTU at L2CAP for browsing channel */
UINT16 avrc_ct_cat; /* AVRCP controller categories */
UINT16 avrc_tg_cat; /* AVRCP target categories */
UINT16 avrc_snk_ct_cat; /* AVRCP controller categories as SNK */
UINT16 avrc_snk_tg_cat; /* AVRCP target categories SNK */
UINT16 avrc_src_ct_cat; /* AVRCP controller categories as SRC */
UINT16 avrc_src_tg_cat; /* AVRCP target categories as SRC */
UINT16 sig_mtu; /* AVDTP signaling channel MTU at L2CAP */
UINT16 audio_mtu; /* AVDTP audio transport channel MTU at L2CAP */
const UINT16 *p_audio_flush_to;/* AVDTP audio transport channel flush timeout */

View file

@ -71,7 +71,7 @@ const static uint16_t cs_psth_allowed_cmd[8] = {
0x0078, /* 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 */
0x1b3F, /* bit mask: 0=POWER, 1=VOL_UP, 2=VOL_DOWN, 3=MUTE,
0x1b7F, /* 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 */
@ -635,6 +635,16 @@ static void handle_rc_get_caps_rsp (tAVRC_GET_CAPS_RSP *rsp)
}
}
static void handle_rc_set_absolute_volume_rsp(tAVRC_SET_VOLUME_RSP *rsp)
{
esp_avrc_ct_cb_param_t param;
memset(&param, 0, sizeof(esp_avrc_ct_cb_param_t));
param.set_volume_rsp.volume = rsp->volume;
btc_avrc_ct_cb_to_app(ESP_AVRC_CT_SET_ABSOLUTE_VOLUME_RSP_EVT, &param);
}
/***************************************************************************
* Function handle_rc_metamsg_cmd
*
@ -822,6 +832,11 @@ static void handle_rc_metamsg_rsp (tBTA_AV_META_MSG *p_meta_msg)
handle_rc_get_caps_rsp(&avrc_response.get_caps);
}
break;
case AVRC_PDU_SET_ABSOLUTE_VOLUME:
if (vendor_msg->hdr.ctype == AVRC_RSP_ACCEPT) {
handle_rc_set_absolute_volume_rsp(&avrc_response.volume);
}
break;
default:
BTC_TRACE_WARNING("%s: unhandled meta rsp: pdu 0x%x", __FUNCTION__, avrc_response.rsp.pdu);
}
@ -1055,15 +1070,15 @@ static bt_status_t btc_avrc_ct_send_set_player_value_cmd(uint8_t tl, uint8_t att
avrc_cmd.set_app_val.p_vals = &values;
avrc_cmd.set_app_val.pdu = AVRC_PDU_SET_PLAYER_APP_VALUE;
status = AVRC_BldCommand(&avrc_cmd, &p_msg);
if (status == AVRC_STS_NO_ERROR) {
if (btc_rc_cb.rc_features & BTA_AV_FEAT_METADATA) {
if (btc_rc_cb.rc_features & BTA_AV_FEAT_METADATA) {
status = AVRC_BldCommand(&avrc_cmd, &p_msg);
if (status == AVRC_STS_NO_ERROR) {
BTA_AvMetaCmd(btc_rc_cb.rc_handle, tl, BTA_AV_CMD_CTRL, p_msg);
status = BT_STATUS_SUCCESS;
} else {
status = BT_STATUS_FAIL;
BTC_TRACE_DEBUG("%s: feature not supported", __FUNCTION__);
}
} else {
status = BT_STATUS_FAIL;
BTC_TRACE_DEBUG("%s: feature not supported", __FUNCTION__);
}
#else
@ -1088,15 +1103,15 @@ static bt_status_t btc_avrc_ct_send_get_rn_caps_cmd(uint8_t tl)
avrc_cmd.get_caps.pdu = AVRC_PDU_GET_CAPABILITIES;
avrc_cmd.get_caps.capability_id = AVRC_CAP_EVENTS_SUPPORTED;
status = AVRC_BldCommand(&avrc_cmd, &p_msg);
if (status == AVRC_STS_NO_ERROR) {
if (btc_rc_cb.rc_features & BTA_AV_FEAT_METADATA) {
if (btc_rc_cb.rc_features & BTA_AV_FEAT_METADATA) {
status = AVRC_BldCommand(&avrc_cmd, &p_msg);
if (status == AVRC_STS_NO_ERROR) {
BTA_AvMetaCmd(btc_rc_cb.rc_handle, tl, AVRC_CMD_STATUS, p_msg);
status = BT_STATUS_SUCCESS;
} else {
status = BT_STATUS_FAIL;
BTC_TRACE_DEBUG("%s: feature not supported", __FUNCTION__);
}
} else {
status = BT_STATUS_FAIL;
BTC_TRACE_DEBUG("%s: feature not supported", __FUNCTION__);
}
#else
@ -1122,15 +1137,48 @@ static bt_status_t btc_avrc_ct_send_register_notification_cmd(uint8_t tl, uint8_
avrc_cmd.reg_notif.param = event_parameter;
avrc_cmd.reg_notif.pdu = AVRC_PDU_REGISTER_NOTIFICATION;
status = AVRC_BldCommand(&avrc_cmd, &p_msg);
if (status == AVRC_STS_NO_ERROR) {
if (btc_rc_cb.rc_features & BTA_AV_FEAT_METADATA) {
if (btc_rc_cb.rc_features & BTA_AV_FEAT_METADATA) {
status = AVRC_BldCommand(&avrc_cmd, &p_msg);
if (status == AVRC_STS_NO_ERROR) {
BTA_AvMetaCmd(btc_rc_cb.rc_handle, tl, AVRC_CMD_NOTIF, p_msg);
status = BT_STATUS_SUCCESS;
} else {
status = BT_STATUS_FAIL;
BTC_TRACE_DEBUG("%s: feature not supported", __FUNCTION__);
}
} else {
status = BT_STATUS_FAIL;
BTC_TRACE_DEBUG("%s: feature not supported", __FUNCTION__);
}
#else
BTC_TRACE_DEBUG("%s: feature not enabled", __FUNCTION__);
#endif
return status;
}
static bt_status_t btc_avrc_ct_send_set_absolute_volume_cmd(uint8_t tl, uint8_t volume)
{
tAVRC_STS status = BT_STATUS_UNSUPPORTED;
#if (AVRC_METADATA_INCLUDED == TRUE)
CHECK_ESP_RC_CONNECTED;
tAVRC_COMMAND avrc_cmd = {0};
BT_HDR *p_msg = NULL;
avrc_cmd.volume.opcode = AVRC_OP_VENDOR;
avrc_cmd.volume.status = AVRC_STS_NO_ERROR;
avrc_cmd.volume.volume = volume;
avrc_cmd.volume.pdu = AVRC_PDU_SET_ABSOLUTE_VOLUME;
if (btc_rc_cb.rc_features & BTA_AV_FEAT_METADATA) {
status = AVRC_BldCommand(&avrc_cmd, &p_msg);
if (status == AVRC_STS_NO_ERROR) {
BTA_AvMetaCmd(btc_rc_cb.rc_handle, tl, AVRC_CMD_CTRL, p_msg);
status = BT_STATUS_SUCCESS;
}
} else {
status = BT_STATUS_FAIL;
BTC_TRACE_DEBUG("%s: feature not supported", __FUNCTION__);
}
#else
@ -1164,15 +1212,15 @@ static bt_status_t btc_avrc_ct_send_metadata_cmd (uint8_t tl, uint8_t attr_mask)
avrc_cmd.get_elem_attrs.num_attr = index;
status = AVRC_BldCommand(&avrc_cmd, &p_msg);
if (status == AVRC_STS_NO_ERROR) {
if (btc_rc_cb.rc_features & BTA_AV_FEAT_METADATA) {
if (btc_rc_cb.rc_features & BTA_AV_FEAT_METADATA) {
status = AVRC_BldCommand(&avrc_cmd, &p_msg);
if (status == AVRC_STS_NO_ERROR) {
BTA_AvMetaCmd(btc_rc_cb.rc_handle, tl, AVRC_CMD_STATUS, p_msg);
status = BT_STATUS_SUCCESS;
} else {
status = BT_STATUS_FAIL;
BTC_TRACE_DEBUG("%s: feature not supported", __FUNCTION__);
}
} else {
status = BT_STATUS_FAIL;
BTC_TRACE_DEBUG("%s: feature not supported", __FUNCTION__);
}
#else
@ -1338,6 +1386,10 @@ void btc_avrc_ct_call_handler(btc_msg_t *msg)
btc_avrc_ct_send_set_player_value_cmd(arg->ps_cmd.tl, arg->ps_cmd.attr_id, arg->ps_cmd.value_id);
break;
}
case BTC_AVRC_CTRL_API_SND_SET_ABSOLUTE_VOLUME_EVT: {
btc_avrc_ct_send_set_absolute_volume_cmd(arg->set_abs_vol_cmd.tl, arg->set_abs_vol_cmd.volume);
break;
}
default:
BTC_TRACE_WARNING("%s : unhandled event: %d\n", __FUNCTION__, msg->act);
}

View file

@ -38,7 +38,8 @@ typedef enum {
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_SND_SET_PLAYER_SETTING_EVT
BTC_AVRC_CTRL_API_SND_SET_PLAYER_SETTING_EVT,
BTC_AVRC_CTRL_API_SND_SET_ABSOLUTE_VOLUME_EVT
} btc_avrc_act_t;
typedef struct {
@ -68,6 +69,14 @@ typedef struct {
uint8_t tl;
} get_caps_cmd_t;
#define BTC_AVRC_MIN_VOLUME 0x00
#define BTC_AVRC_MAX_VOLUME 0x7f
typedef struct {
uint8_t tl;
uint8_t volume;
} set_abs_vol_cmd_t;
/* btc_avrc_args_t */
typedef union {
pt_cmd_t pt_cmd;
@ -75,6 +84,7 @@ typedef union {
rn_cmd_t rn_cmd;
ps_cmd_t ps_cmd;
get_caps_cmd_t get_caps_cmd;
set_abs_vol_cmd_t set_abs_vol_cmd;
} btc_avrc_args_t;
/* btc_avrc_tg_act_t */

View file

@ -27,6 +27,11 @@
#include "esp_avrc_api.h"
#define BT_AV_TAG "BT_AV"
#define BT_RC_CT_TAG "RCCT"
// AVRCP used transaction label
#define APP_RC_CT_TL_GET_CAPS (0)
#define APP_RC_CT_TL_RN_VOLUME_CHANGE (1)
/* event for handler "bt_av_hdl_stack_up */
enum {
@ -63,11 +68,17 @@ static void bt_app_a2d_cb(esp_a2d_cb_event_t event, esp_a2d_cb_param_t *param);
/// callback function for A2DP source audio data stream
static int32_t bt_app_a2d_data_cb(uint8_t *data, int32_t len);
/// callback function for AVRCP controller
static void bt_app_rc_ct_cb(esp_avrc_ct_cb_event_t event, esp_avrc_ct_cb_param_t *param);
static void a2d_app_heart_beat(void *arg);
/// A2DP application state machine
static void bt_app_av_sm_hdlr(uint16_t event, void *param);
/// avrc CT event handler
static void bt_av_hdl_avrc_ct_evt(uint16_t event, void *p_param);
/* A2DP application state machine handler for each state */
static void bt_app_av_state_unconnected(uint16_t event, void *param);
static void bt_app_av_state_connecting(uint16_t event, void *param);
@ -81,7 +92,7 @@ static int s_media_state = APP_AV_MEDIA_STATE_IDLE;
static int s_intv_cnt = 0;
static int s_connecting_intv = 0;
static uint32_t s_pkt_cnt = 0;
static esp_avrc_rn_evt_cap_mask_t s_avrc_peer_rn_cap;
static TimerHandle_t s_tmr;
static char *bda2str(esp_bd_addr_t bda, char *str, size_t size)
@ -322,6 +333,14 @@ static void bt_av_hdl_stack_evt(uint16_t event, void *p_param)
/* register GAP callback function */
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);
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 source */
esp_a2d_register_callback(&bt_app_a2d_cb);
esp_a2d_source_register_data_callback(bt_app_a2d_data_cb);
@ -578,3 +597,98 @@ static void bt_app_av_state_disconnecting(uint16_t event, void *param)
break;
}
}
static void bt_app_rc_ct_cb(esp_avrc_ct_cb_event_t event, esp_avrc_ct_cb_param_t *param)
{
switch (event) {
case ESP_AVRC_CT_METADATA_RSP_EVT:
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:
case ESP_AVRC_CT_GET_RN_CAPABILITIES_RSP_EVT:
case ESP_AVRC_CT_SET_ABSOLUTE_VOLUME_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_RC_CT_TAG, "Invalid AVRC event: %d", event);
break;
}
}
static void bt_av_volume_changed(void)
{
if (esp_avrc_rn_evt_bit_mask_operation(ESP_AVRC_BIT_MASK_OP_TEST, &s_avrc_peer_rn_cap,
ESP_AVRC_RN_VOLUME_CHANGE)) {
esp_avrc_ct_send_register_notification_cmd(APP_RC_CT_TL_RN_VOLUME_CHANGE, ESP_AVRC_RN_VOLUME_CHANGE, 0);
}
}
void bt_av_notify_evt_handler(uint8_t event_id, esp_avrc_rn_param_t *event_parameter)
{
switch (event_id) {
case ESP_AVRC_RN_VOLUME_CHANGE:
ESP_LOGI(BT_RC_CT_TAG, "Volume changed: %d", event_parameter->volume);
ESP_LOGI(BT_RC_CT_TAG, "Set absolute volume: volume %d", event_parameter->volume + 5);
esp_avrc_ct_send_set_absolute_volume_cmd(APP_RC_CT_TL_RN_VOLUME_CHANGE, event_parameter->volume + 5);
bt_av_volume_changed();
break;
}
}
static void bt_av_hdl_avrc_ct_evt(uint16_t event, void *p_param)
{
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_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) {
// 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_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_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_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_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_volume_changed();
break;
}
case ESP_AVRC_CT_SET_ABSOLUTE_VOLUME_RSP_EVT: {
ESP_LOGI(BT_RC_CT_TAG, "Set absolute volume rsp: volume %d", rc->set_volume_rsp.volume);
break;
}
default:
ESP_LOGE(BT_RC_CT_TAG, "%s unhandled evt %d", __func__, event);
break;
}
}