diff --git a/components/bt/host/bluedroid/api/esp_avrc_api.c b/components/bt/host/bluedroid/api/esp_avrc_api.c index 9b9e6616c..cbfa71a03 100644 --- a/components/bt/host/bluedroid/api/esp_avrc_api.c +++ b/components/bt/host/bluedroid/api/esp_avrc_api.c @@ -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 */ diff --git a/components/bt/host/bluedroid/api/include/api/esp_avrc_api.h b/components/bt/host/bluedroid/api/include/api/esp_avrc_api.h index 814f2be33..e75eb637d 100644 --- a/components/bt/host/bluedroid/api/include/api/esp_avrc_api.h +++ b/components/bt/host/bluedroid/api/include/api/esp_avrc_api.h @@ -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 diff --git a/components/bt/host/bluedroid/bta/av/bta_av_cfg.c b/components/bt/host/bluedroid/bta/av/bta_av_cfg.c index b8a86831c..9f39239f5 100644 --- a/components/bt/host/bluedroid/bta/av/bta_av_cfg.c +++ b/components/bt/host/bluedroid/bta/av/bta_av_cfg.c @@ -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 */ diff --git a/components/bt/host/bluedroid/bta/av/bta_av_main.c b/components/bt/host/bluedroid/bta/av/bta_av_main.c index bc352c8fa..f66936526 100644 --- a/components/bt/host/bluedroid/bta/av/bta_av_main.c +++ b/components/bt/host/bluedroid/bta/av/bta_av_main.c @@ -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 } } diff --git a/components/bt/host/bluedroid/bta/include/bta/bta_av_api.h b/components/bt/host/bluedroid/bta/include/bta/bta_av_api.h index 351c0bc61..5efc2662c 100644 --- a/components/bt/host/bluedroid/bta/include/bta/bta_av_api.h +++ b/components/bt/host/bluedroid/bta/include/bta/bta_av_api.h @@ -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 */ diff --git a/components/bt/host/bluedroid/btc/profile/std/avrc/btc_avrc.c b/components/bt/host/bluedroid/btc/profile/std/avrc/btc_avrc.c index 28ecb85e6..0e4d0e492 100644 --- a/components/bt/host/bluedroid/btc/profile/std/avrc/btc_avrc.c +++ b/components/bt/host/bluedroid/btc/profile/std/avrc/btc_avrc.c @@ -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(¶m, 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, ¶m); +} + /*************************************************************************** * 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); } diff --git a/components/bt/host/bluedroid/btc/profile/std/include/btc_avrc.h b/components/bt/host/bluedroid/btc/profile/std/include/btc_avrc.h index ec98c5ee5..2ff164b50 100644 --- a/components/bt/host/bluedroid/btc/profile/std/include/btc_avrc.h +++ b/components/bt/host/bluedroid/btc/profile/std/include/btc_avrc.h @@ -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 */ diff --git a/examples/bluetooth/a2dp_source/main/main.c b/examples/bluetooth/a2dp_source/main/main.c index dbf7618b8..a3971f8cd 100644 --- a/examples/bluetooth/a2dp_source/main/main.c +++ b/examples/bluetooth/a2dp_source/main/main.c @@ -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; + } +} \ No newline at end of file