if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) {
return ESP_ERR_INVALID_STATE;
}
-
+
if (callback == NULL) {
return ESP_FAIL;
}
}
btc_msg_t msg;
-
+
msg.sig = BTC_SIG_API_CALL;
msg.pid = BTC_PID_AVRC;
msg.act = BTC_AVRC_CTRL_API_INIT_EVT;
}
btc_msg_t msg;
-
+
msg.sig = BTC_SIG_API_CALL;
msg.pid = BTC_PID_AVRC;
msg.act = BTC_AVRC_CTRL_API_DEINIT_EVT;
return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL;
}
+esp_err_t esp_avrc_ct_send_set_player_value_cmd(uint8_t tl, uint8_t attr_id, uint8_t value_id)
+{
+ if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) {
+ return ESP_ERR_INVALID_STATE;
+ }
+
+ if (tl >= 16 || attr_id > ESP_AVRC_PS_MAX_ATTR - 1) {
+ return ESP_FAIL;
+ }
+
+ 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;
+
+ btc_avrc_args_t arg;
+ memset(&arg, 0, sizeof(btc_avrc_args_t));
+
+ arg.ps_cmd.tl = tl;
+ arg.ps_cmd.attr_id = attr_id;
+ arg.ps_cmd.value_id = value_id;
+
+ /* 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)
+{
+ if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) {
+ return ESP_ERR_INVALID_STATE;
+ }
+
+ if (tl >= 16 || event_id > ESP_AVRC_RN_MAX_EVT - 1) {
+ return ESP_FAIL;
+ }
+
+ btc_msg_t msg;
+ msg.sig = BTC_SIG_API_CALL;
+ msg.pid = BTC_PID_AVRC;
+ msg.act = BTC_AVRC_NOTIFY_API_SND_REG_NOTIFY_EVT;
+
+ btc_avrc_args_t arg;
+ memset(&arg, 0, sizeof(btc_avrc_args_t));
+
+ arg.rn_cmd.tl = tl;
+ arg.rn_cmd.event_id = event_id;
+ arg.rn_cmd.event_parameter = event_parameter;
+
+ /* 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;
+ }
+
+ btc_msg_t msg;
+ msg.sig = BTC_SIG_API_CALL;
+ msg.pid = BTC_PID_AVRC;
+ msg.act = BTC_AVRC_STATUS_API_SND_META_EVT;
+
+ btc_avrc_args_t arg;
+ memset(&arg, 0, sizeof(btc_avrc_args_t));
+
+ arg.md_cmd.tl = tl;
+ arg.md_cmd.attr_mask = attr_mask;
+
+ /* 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_passthrough_cmd(uint8_t tl, uint8_t key_code, uint8_t key_state)
{
if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) {
return ESP_ERR_INVALID_STATE;
}
+ if (tl >= 16 || key_state > ESP_AVRC_PT_CMD_STATE_RELEASED) {
+ return ESP_FAIL;
+ }
+
btc_msg_t msg;
msg.sig = BTC_SIG_API_CALL;
msg.pid = BTC_PID_AVRC;
arg.pt_cmd.tl = tl;
arg.pt_cmd.key_code = key_code;
arg.pt_cmd.key_state = key_state;
-
+
/* 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_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_BACKWARD = 0x4C, /*!< backward */
+ ESP_AVRC_PT_CMD_REWIND = 0x48, /*!< rewind */
+ ESP_AVRC_PT_CMD_FAST_FORWARD = 0x49 /*!< fast forward */
} esp_avrc_pt_cmd_t;
/// AVRC passthrough command state
typedef enum {
ESP_AVRC_CT_CONNECTION_STATE_EVT = 0, /*!< connection state changed event */
ESP_AVRC_CT_PASSTHROUGH_RSP_EVT = 1, /*!< passthrough response event */
+ ESP_AVRC_CT_METADATA_RSP_EVT = 2, /*!< metadata response event */
+ ESP_AVRC_CT_PLAY_STATUS_RSP_EVT = 3, /*!< play status response event */
+ ESP_AVRC_CT_CHANGE_NOTIFY_EVT = 4, /*!< notification event */
ESP_AVRC_CT_MAX_EVT
} esp_avrc_ct_cb_event_t;
+//AVRC metadata attribute mask
+typedef enum {
+ ESP_AVRC_MD_ATTR_TITLE = 0x1, /*!< title of the playing track */
+ ESP_AVRC_MD_ATTR_ARTIST = 0x2, /*!< track artist */
+ ESP_AVRC_MD_ATTR_ALBUM = 0x4, /*!< album name */
+ ESP_AVRC_MD_ATTR_TRACK_NUM = 0x8, /*!< track position on the album */
+ ESP_AVRC_MD_ATTR_NUM_TRACKS = 0x10, /*!< number of tracks on the album */
+ ESP_AVRC_MD_ATTR_GENRE = 0x20, /*!< track genre */
+ ESP_AVRC_MD_ATTR_PLAYING_TIME = 0x40 /*!< total album playing time in miliseconds */
+} esp_avrc_md_attr_mask_t;
+
+//AVRC event notification ids
+typedef enum {
+ ESP_AVRC_RN_PLAY_STATUS_CHANGE = 0x01, /*!< track status change, eg. from playing to paused */
+ ESP_AVRC_RN_TRACK_CHANGE = 0x02, /*!< new track is loaded */
+ ESP_AVRC_RN_TRACK_REACHED_END = 0x03, /*!< current track reached end */
+ ESP_AVRC_RN_TRACK_REACHED_START = 0x04, /*!< current track reached start position */
+ ESP_AVRC_RN_PLAY_POS_CHANGED = 0x05, /*!< track playing position changed */
+ 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_MAX_EVT
+} esp_avrc_rn_event_ids_t;
+
+//AVRC player setting ids
+typedef enum {
+ ESP_AVRC_PS_EQUALIZER = 0x01, /*!< equalizer, on or off */
+ ESP_AVRC_PS_REPEAT_MODE = 0x02, /*!< repeat mode */
+ ESP_AVRC_PS_SHUFFLE_MODE = 0x03, /*!< shuffle mode */
+ ESP_AVRC_PS_SCAN_MODE = 0x04, /*!< scan mode on or off */
+ ESP_AVRC_PS_MAX_ATTR
+} esp_avrc_ps_attr_ids_t;
+
+//AVRC equalizer modes
+typedef enum {
+ ESP_AVRC_PS_EQUALIZER_OFF = 0x1,
+ ESP_AVRC_PS_EQUALIZER_ON = 0x2
+} esp_avrc_ps_eq_value_ids_t;
+
+//AVRC repeat modes
+typedef enum {
+ ESP_AVRC_PS_REPEAT_OFF = 0x1,
+ ESP_AVRC_PS_REPEAT_SINGLE = 0x2,
+ ESP_AVRC_PS_REPEAT_GROUP = 0x3
+} esp_avrc_ps_rpt_value_ids_t;
+
+
+//AVRC shuffle modes
+typedef enum {
+ ESP_AVRC_PS_SHUFFLE_OFF = 0x1,
+ ESP_AVRC_PS_SHUFFLE_ALL = 0x2,
+ ESP_AVRC_PS_SHUFFLE_GROUP = 0x3
+} esp_avrc_ps_shf_value_ids_t;
+
+//AVRC scan modes
+typedef enum {
+ ESP_AVRC_PS_SCAN_OFF = 0x1,
+ ESP_AVRC_PS_SCAN_ALL = 0x2,
+ ESP_AVRC_PS_SCAN_GROUP = 0x3
+} esp_avrc_ps_scn_value_ids_t;
+
/// AVRC controller callback parameters
typedef union {
/**
*/
struct avrc_ct_conn_stat_param {
bool connected; /*!< whether AVRC connection is set up */
- uint32_t feat_mask; /*!< AVRC feature mask of remote device */
+ uint32_t feat_mask; /*!< AVRC feature mask of remote device */
esp_bd_addr_t remote_bda; /*!< remote bluetooth device address */
} conn_stat; /*!< AVRC connection status */
-
+
/**
* @brief ESP_AVRC_CT_PASSTHROUGH_RSP_EVT
*/
uint8_t key_code; /*!< passthrough command code */
uint8_t key_state; /*!< 0 for PRESSED, 1 for RELEASED */
} psth_rsp; /*!< passthrough command response */
+
+ /**
+ * @brief ESP_AVRC_CT_METADATA_RSP_EVT
+ */
+ struct avrc_ct_meta_rsp_param {
+ uint8_t attr_id; /*!< id of metadata attribute */
+ uint8_t *attr_text; /*!< attribute itself */
+ int attr_length; /*!< attribute character length */
+ } meta_rsp; /*!< metadata attributes response */
+
+ /**
+ * @brief ESP_AVRC_CT_CHANGE_NOTIFY_EVT
+ */
+ struct avrc_ct_change_notify_param {
+ uint8_t event_id; /*!< id of AVRC event notification */
+ uint32_t event_parameter; /*!< event notification parameter */
+ } change_ntf; /*!< notifications */
+
} esp_avrc_ct_cb_param_t;
/**
* @brief Register application callbacks to AVRCP module; for now only AVRCP Controller
- * role is supported. This function should be called after esp_bluedroid_enable()
+ * role is supported. This function should be called after esp_bluedroid_enable()
* completes successfully
- *
+ *
* @param[in] callback: AVRCP controller callback function
*
* @return
* @brief Initialize the bluetooth AVRCP controller module, This function should be called
* after esp_bluedroid_enable() completes successfully
*
- * @return
+ * @return
* - ESP_OK: success
* - ESP_INVALID_STATE: if bluetooth stack is not yet enabled
* - ESP_FAIL: others
* @brief De-initialize AVRCP controller module. This function should be called after
* after esp_bluedroid_enable() completes successfully
*
- * @return
+ * @return
* - ESP_OK: success
* - ESP_INVALID_STATE: if bluetooth stack is not yet enabled
* - ESP_FAIL: others
*/
esp_err_t esp_avrc_ct_deinit(void);
+/* add description */
+esp_err_t esp_avrc_ct_send_set_player_value_cmd(uint8_t tl, uint8_t attr_id, uint8_t value_id);
+
+/**
+ * @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
+ *
+ * @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
+ * @return
+ * - ESP_OK: success
+ * - ESP_INVALID_STATE: if bluetooth stack is not yet enabled
+ * - ESP_FAIL: others
+ */
+esp_err_t esp_avrc_ct_send_register_notification_cmd(uint8_t tl, uint8_t event_id, uint32_t event_parameter);
+
+
+/**
+ * @brief Send metadata 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] attr_mask : mask of attributes, e.g. ESP_AVRC_MD_ATTR_ID_TITLE | ESP_AVRC_MD_ATTR_ID_ARTIST.
+ *
+ * @return
+ * - ESP_OK: success
+ * - ESP_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);
+
/**
* @brief Send passthrough command to AVRCP target, This function should be called after
* @param[in] key_state : passthrough command key state, ESP_AVRC_PT_CMD_STATE_PRESSED or
* ESP_AVRC_PT_CMD_STATE_RELEASED
*
- * @return
+ * @return
* - ESP_OK: success
* - ESP_INVALID_STATE: if bluetooth stack is not yet enabled
* - ESP_FAIL: others
* auto-suspend av streaming on AG events(SCO or Call). The suspend shall
* be initiated by the app/audioflinger layers */
BTA_AvEnable(BTA_SEC_AUTHENTICATE, (BTA_AV_FEAT_NO_SCO_SSPD)
- // | BTA_AV_FEAT_RCTG | BTA_AV_FEAT_METADATA | BTA_AV_FEAT_VENDOR
+ | 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);
#include "btc_manage.h"
#include "esp_avrc_api.h"
#include "mutex.h"
+#include "allocator.h"
#if BTC_AV_INCLUDED
static void handle_rc_connect (tBTA_AV_RC_OPEN *p_rc_open);
static void handle_rc_disconnect (tBTA_AV_RC_CLOSE *p_rc_close);
static void handle_rc_passthrough_rsp ( tBTA_AV_REMOTE_RSP *p_remote_rsp);
+static void handle_rc_metadata_rsp ( tBTA_AV_META_MSG *p_remote_rsp);
/*****************************************************************************
** Static variables
#endif
}
+static void handle_rc_attributes_rsp ( tAVRC_MSG_VENDOR *vendor_msg)
+{
+ uint8_t attr_count = vendor_msg->p_vendor_data[4];
+ int attr_index = 5;
+ int attr_length = 0;
+ uint32_t attr_id = 0;
+
+ //Check if there are any attributes
+ if (attr_count < 1) {
+ return;
+ }
+
+ esp_avrc_ct_cb_param_t param[attr_count];
+ memset(¶m[0], 0, sizeof(esp_avrc_ct_cb_param_t) * attr_count);
+
+ for (int i = 0; i < attr_count; i++) {
+ attr_length = (int) vendor_msg->p_vendor_data[7 + attr_index] | vendor_msg->p_vendor_data[6 + attr_index] << 8;
+
+ //Received attribute text is not null terminated, so it's useful to know it's length
+ param[i].meta_rsp.attr_length = attr_length;
+ param[i].meta_rsp.attr_text = &vendor_msg->p_vendor_data[8 + attr_index];
+
+ attr_id = vendor_msg->p_vendor_data[3 + attr_index] |
+ vendor_msg->p_vendor_data[2 + attr_index] << 8 | vendor_msg->p_vendor_data[1 + attr_index] << 16 |
+ vendor_msg->p_vendor_data[attr_index] << 24;
+
+ //Convert to mask id
+ param[i].meta_rsp.attr_id = (1 << (attr_id - 1));
+
+ btc_avrc_ct_cb_to_app(ESP_AVRC_CT_METADATA_RSP_EVT, ¶m[i]);
+
+ attr_index += (int) vendor_msg->p_vendor_data[7 + attr_index] + 8;
+ }
+}
+
+static void handle_rc_notification_rsp ( tAVRC_MSG_VENDOR *vendor_msg)
+{
+ esp_avrc_ct_cb_param_t param;
+
+ param.change_ntf.event_id = vendor_msg->p_vendor_data[4];
+
+ param.change_ntf.event_parameter = vendor_msg->p_vendor_data[5] << 24 | vendor_msg->p_vendor_data[6] << 16 |
+ vendor_msg->p_vendor_data[7] << 8 | vendor_msg->p_vendor_data[8];
+
+ btc_avrc_ct_cb_to_app(ESP_AVRC_CT_CHANGE_NOTIFY_EVT, ¶m);
+}
+
+/***************************************************************************
+ * Function handle_rc_metadata_rsp
+ *
+ * - Argument: tBTA_AV_META_MSG metadata command response
+ *
+ * - Description: Vendor metadata response handler
+ *
+ ***************************************************************************/
+static void handle_rc_metadata_rsp ( tBTA_AV_META_MSG *p_remote_rsp)
+{
+#if (AVRC_METADATA_INCLUDED == TRUE)
+ tAVRC_MSG *avrc_msg = p_remote_rsp->p_msg;
+ tAVRC_MSG_VENDOR *vendor_msg = &avrc_msg->vendor;
+
+ //Check what type of metadata was received
+ switch (vendor_msg->hdr.ctype) {
+ case AVRC_RSP_CHANGED:
+ if (vendor_msg->p_vendor_data[0] == AVRC_PDU_REGISTER_NOTIFICATION) {
+ handle_rc_notification_rsp(vendor_msg);
+ }
+ break;
+
+ case AVRC_RSP_IMPL_STBL:
+ if (vendor_msg->p_vendor_data[0] == AVRC_PDU_GET_ELEMENT_ATTR) {
+ handle_rc_attributes_rsp(vendor_msg);
+ }
+ break;
+ }
+#else
+ LOG_ERROR("%s AVRCP metadata is not enabled", __FUNCTION__);
+#endif
+}
+
/***************************************************************************
* Function handle_rc_passthrough_rsp
*
}
break;
+ case BTA_AV_META_MSG_EVT: {
+ handle_rc_metadata_rsp(&(p_data->meta_msg));
+ }
+ break;
+
// below events are not handled for now
- case BTA_AV_META_MSG_EVT:
case BTA_AV_REMOTE_CMD_EVT:
default:
LOG_DEBUG("Unhandled RC event : 0x%x", event);
LOG_INFO("## %s ## completed", __FUNCTION__);
}
-static bt_status_t btc_avrc_ct_send_passthrough_cmd(uint8_t tl, uint8_t key_code, uint8_t key_state)
+static bt_status_t btc_avrc_ct_send_set_player_value_cmd(uint8_t tl, uint8_t attr_id, uint8_t value_id)
+{
+ 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;
+ tAVRC_APP_SETTING values = {0};
+
+ values.attr_id = attr_id;
+ values.attr_val = value_id;
+
+ avrc_cmd.set_app_val.opcode = AVRC_OP_VENDOR;
+ avrc_cmd.set_app_val.status = AVRC_STS_NO_ERROR;
+ avrc_cmd.set_app_val.num_val = 1;
+ 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_vb.rc_features & BTA_AV_FEAT_METADATA) {
+ BTA_AvMetaCmd(btc_rc_vb.rc_handle, tl, BTA_AV_CMD_CTRL, p_msg);
+ status = BT_STATUS_SUCCESS;
+ } else {
+ status = BT_STATUS_FAIL;
+ LOG_DEBUG("%s: feature not supported", __FUNCTION__);
+ }
+ }
+
+#else
+ LOG_DEBUG("%s: feature not enabled", __FUNCTION__);
+#endif
+
+ return status;
+}
+
+static bt_status_t btc_avrc_ct_send_register_notification_cmd(uint8_t tl, uint8_t event_id, uint32_t event_parameter)
{
tAVRC_STS status = BT_STATUS_UNSUPPORTED;
- if (tl >= 16 ||
- key_state > ESP_AVRC_PT_CMD_STATE_RELEASED) {
- return ESP_ERR_INVALID_ARG;
+
+#if (AVRC_METADATA_INCLUDED == TRUE)
+ CHECK_ESP_RC_CONNECTED;
+
+ tAVRC_COMMAND avrc_cmd = {0};
+ BT_HDR *p_msg = NULL;
+
+ avrc_cmd.reg_notif.opcode = AVRC_OP_VENDOR;
+ avrc_cmd.reg_notif.status = AVRC_STS_NO_ERROR;
+ avrc_cmd.reg_notif.event_id = event_id;
+ 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_vb.rc_features & BTA_AV_FEAT_METADATA) {
+ BTA_AvMetaCmd(btc_rc_vb.rc_handle, tl, AVRC_CMD_NOTIF, p_msg);
+ status = BT_STATUS_SUCCESS;
+ } else {
+ status = BT_STATUS_FAIL;
+ LOG_DEBUG("%s: feature not supported", __FUNCTION__);
+ }
}
+
+#else
+ LOG_DEBUG("%s: feature not enabled", __FUNCTION__);
+#endif
+
+ return status;
+}
+
+static bt_status_t btc_avrc_ct_send_metadata_cmd (uint8_t tl, uint8_t attr_mask)
+{
+ tAVRC_STS status = BT_STATUS_UNSUPPORTED;
+
+#if (AVRC_METADATA_INCLUDED == TRUE)
+ CHECK_ESP_RC_CONNECTED;
+ uint32_t index = 0;
+
+ tAVRC_COMMAND avrc_cmd = {0};
+ BT_HDR *p_msg = NULL;
+
+ avrc_cmd.get_elem_attrs.opcode = AVRC_OP_VENDOR;
+ avrc_cmd.get_elem_attrs.status = AVRC_STS_NO_ERROR;
+ avrc_cmd.get_elem_attrs.pdu = AVRC_PDU_GET_ELEMENT_ATTR;
+
+ for (int count = 0; count < AVRC_MAX_ELEM_ATTR_SIZE; count++) {
+ if ((attr_mask & (1 << count)) > 0) {
+ avrc_cmd.get_elem_attrs.attrs[index] = count + 1;
+ index++;
+ }
+ }
+
+ avrc_cmd.get_elem_attrs.num_attr = index;
+
+ status = AVRC_BldCommand(&avrc_cmd, &p_msg);
+ if (status == AVRC_STS_NO_ERROR) {
+ if (btc_rc_vb.rc_features & BTA_AV_FEAT_METADATA) {
+ BTA_AvMetaCmd(btc_rc_vb.rc_handle, tl, AVRC_CMD_STATUS, p_msg);
+ status = BT_STATUS_SUCCESS;
+ } else {
+ status = BT_STATUS_FAIL;
+ LOG_DEBUG("%s: feature not supported", __FUNCTION__);
+ }
+ }
+
+#else
+ LOG_DEBUG("%s: feature not enabled", __FUNCTION__);
+#endif
+
+ return status;
+}
+
+static bt_status_t btc_avrc_ct_send_passthrough_cmd(uint8_t tl, uint8_t key_code, uint8_t key_state)
+{
+ tAVRC_STS status = BT_STATUS_UNSUPPORTED;
+
#if (AVRC_CTLR_INCLUDED == TRUE)
CHECK_ESP_RC_CONNECTED;
LOG_DEBUG("%s: key-code: %d, key-state: %d", __FUNCTION__,
// todo: callback to application
break;
}
+ case BTC_AVRC_STATUS_API_SND_META_EVT: {
+ btc_avrc_ct_send_metadata_cmd(arg->md_cmd.tl, arg->md_cmd.attr_mask);
+ break;
+ }
+ case BTC_AVRC_NOTIFY_API_SND_REG_NOTIFY_EVT: {
+ btc_avrc_ct_send_register_notification_cmd(arg->rn_cmd.tl, arg->rn_cmd.event_id, arg->rn_cmd.event_parameter);
+ break;
+ }
+ case BTC_AVRC_CTRL_API_SET_PLAYER_SETTING_EVT: {
+ btc_avrc_ct_send_set_player_value_cmd(arg->ps_cmd.tl, arg->ps_cmd.attr_id, arg->ps_cmd.value_id);
+ break;
+ }
default:
LOG_WARN("%s : unhandled event: %d\n", __FUNCTION__, msg->act);
}
typedef enum {
BTC_AVRC_CTRL_API_INIT_EVT = 0,
BTC_AVRC_CTRL_API_DEINIT_EVT,
- BTC_AVRC_CTRL_API_SND_PTCMD_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_NOTIFY_API_SND_REG_NOTIFY_EVT,
+ BTC_AVRC_CTRL_API_SET_PLAYER_SETTING_EVT
} btc_avrc_act_t;
typedef struct {
uint8_t key_state;
} pt_cmd_t;
+typedef struct {
+ uint8_t tl;
+ uint8_t attr_mask;
+} md_cmd_t;
+
+typedef struct {
+ uint8_t tl;
+ uint8_t event_id;
+ uint32_t event_parameter;
+} rn_cmd_t;
+
+typedef struct {
+ uint8_t tl;
+ uint8_t attr_id;
+ uint8_t value_id;
+} ps_cmd_t;
+
/* btc_avrc_args_t */
typedef union {
- // BTC_AVRC_CTRL_API_SND_PT_CMD_EVT
- struct {
- uint8_t tl;
- uint8_t key_code;
- uint8_t key_state;
- } pt_cmd;
+ pt_cmd_t pt_cmd;
+ md_cmd_t md_cmd;
+ rn_cmd_t rn_cmd;
+ ps_cmd_t ps_cmd;
} btc_avrc_args_t;
/** BT-RC Controller callback structure. */
** Otherwise, the error code.
**
*******************************************************************************/
-static tAVRC_STS avrc_bld_vol_change_notfn(BT_HDR *p_pkt)
+static tAVRC_STS avrc_bld_register_change_notfn(UINT8 event_id, UINT32 event_parameter, BT_HDR *p_pkt)
{
UINT8 *p_data, *p_start;
p_data = p_start + 2; /* pdu + rsvd */
/* add fixed length 5 -*/
UINT16_TO_BE_STREAM(p_data, 5);
- UINT8_TO_BE_STREAM(p_data, AVRC_EVT_VOLUME_CHANGE);
- UINT32_TO_BE_STREAM(p_data, 0);
+ UINT8_TO_BE_STREAM(p_data, event_id);
+ UINT32_TO_BE_STREAM(p_data, event_parameter);
p_pkt->len = (p_data - p_start);
return AVRC_STS_NO_ERROR;
}
AVRC_TRACE_API("avrc_bld_init_cmd_buffer: pdu=%x, opcode=%x", p_cmd->pdu, opcode);
UINT16 offset = 0;
- switch (opcode)
- {
+ switch (opcode) {
case AVRC_OP_PASS_THRU:
offset = AVRC_MSG_PASS_THRU_OFFSET;
break;
if (p_pkt) {
UINT8 *p_data, *p_start;
- p_pkt->layer_specific = AVCT_DATA_CTRL;
+ p_pkt->layer_specific = AVCT_DATA_CTRL;
p_pkt->event = opcode;
p_pkt->offset = offset;
p_data = (UINT8 *)(p_pkt + 1) + p_pkt->offset;
return p_pkt;
}
+/*******************************************************************************
+**
+** Function avrc_bld_set_player_value_cmd
+**
+** Description This function builds the Set Player Application Value command.
+**
+** Returns AVRC_STS_NO_ERROR, if the command is built successfully
+** Otherwise, the error code.
+**
+*******************************************************************************/
+static tAVRC_STS avrc_bld_set_player_value_cmd(tAVRC_SET_APP_VALUE_CMD *p_cmd, BT_HDR *p_pkt)
+{
+ UINT8 *p_data, *p_start;
+
+ /* get the existing length, if any, and also the num attributes */
+ p_start = (UINT8 *)(p_pkt + 1) + p_pkt->offset;
+ p_data = p_start + 2; /* pdu + rsvd */
+ /* add length */
+ UINT16_TO_BE_STREAM(p_data, 3);
+ /* Number of attributes */
+ UINT8_TO_BE_STREAM(p_data, 1);
+ UINT8_TO_BE_STREAM(p_data, p_cmd->p_vals->attr_id);
+ UINT8_TO_BE_STREAM(p_data, p_cmd->p_vals->attr_val);
+
+ p_pkt->len = (p_data - p_start);
+ return AVRC_STS_NO_ERROR;
+}
+
+/*******************************************************************************
+**
+** Function avrc_bld_get_element_attr_cmd
+**
+** Description This function builds the Get Element Attribute command.
+**
+** Returns AVRC_STS_NO_ERROR, if the command is built successfully
+** Otherwise, the error code.
+**
+*******************************************************************************/
+static tAVRC_STS avrc_bld_get_element_attr_cmd (tAVRC_GET_ELEM_ATTRS_CMD *p_cmd, BT_HDR *p_pkt)
+{
+ int i;
+ UINT8 *p_data, *p_start;
+
+ AVRC_TRACE_API("avrc_bld_get_element_attr_cmd num_attr: %d", p_cmd->num_attr);
+ /* get the existing length, if any, and also the num attributes */
+ p_start = (UINT8 *)(p_pkt + 1) + p_pkt->offset;
+ p_data = p_start + 2; /* pdu + rsvd */
+ /* add length */
+ UINT16_TO_BE_STREAM(p_data, 8 + 1 /* id + attr count */ + p_cmd->num_attr * sizeof(UINT32));
+ /* Identifier 0x0 (PLAYING) */
+ UINT64_TO_BE_STREAM(p_data, (UINT64)(0));
+ /* Attribute count */
+ UINT8_TO_BE_STREAM(p_data, p_cmd->num_attr);
+
+ for (i = 0; i < p_cmd->num_attr; i++) {
+ AVRC_TRACE_API("avrc_bld_get_element_attr_cmd attr_id: %d", p_cmd->attrs[i]);
+ UINT32_TO_BE_STREAM(p_data, p_cmd->attrs[i]);
+ }
+
+ p_pkt->len = (p_data - p_start);
+ return AVRC_STS_NO_ERROR;
+}
+
/*******************************************************************************
**
** Function AVRC_BldCommand
break;
#endif
- case AVRC_PDU_REGISTER_NOTIFICATION: /* 0x31 */
-#if (AVRC_ADV_CTRL_INCLUDED == TRUE)
- if (AVRC_EVT_VOLUME_CHANGE == p_cmd->reg_notif.event_id) {
- status = avrc_bld_vol_change_notfn(p_pkt);
- }
-#endif
+ case AVRC_PDU_SET_PLAYER_APP_VALUE: /* 0x14 */
+ status = avrc_bld_set_player_value_cmd(&p_cmd->set_app_val, p_pkt);
+ break;
+
+ case AVRC_PDU_GET_ELEMENT_ATTR: /* 0x20 */
+ status = avrc_bld_get_element_attr_cmd(&p_cmd->get_elem_attrs, p_pkt);
break;
+ 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;
}
if (alloc && (status != AVRC_STS_NO_ERROR) ) {
/********************************************************************************
** Macros to get and put bytes to and from a stream (Big Endian format)
*/
+#define UINT64_TO_BE_STREAM(p, u64) {*(p)++ = (UINT8)((u64) >> 56); *(p)++ = (UINT8)((u64) >> 48); *(p)++ = (UINT8)((u64) >> 40); *(p)++ = (UINT8)((u64) >> 32); *(p)++ = (UINT8)((u64) >> 24); *(p)++ = (UINT8)((u64) >> 16); *(p)++ = (UINT8)((u64) >> 8); *(p)++ = (UINT8)(u64); }
#define UINT32_TO_BE_STREAM(p, u32) {*(p)++ = (UINT8)((u32) >> 24); *(p)++ = (UINT8)((u32) >> 16); *(p)++ = (UINT8)((u32) >> 8); *(p)++ = (UINT8)(u32); }
#define UINT24_TO_BE_STREAM(p, u24) {*(p)++ = (UINT8)((u24) >> 16); *(p)++ = (UINT8)((u24) >> 8); *(p)++ = (UINT8)(u24);}
#define UINT16_TO_BE_STREAM(p, u16) {*(p)++ = (UINT8)((u16) >> 8); *(p)++ = (UINT8)(u16);}
/* avrc event handler */
static void bt_av_hdl_avrc_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;
}
}
+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);
+ memcpy(attr_text, rc->meta_rsp.attr_text, rc->meta_rsp.attr_length);
+ attr_text[rc->meta_rsp.attr_length] = 0;
+
+ rc->meta_rsp.attr_text = attr_text;
+}
+
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:
+ bt_app_alloc_meta_buffer(param);
case ESP_AVRC_CT_CONNECTION_STATE_EVT:
- case ESP_AVRC_CT_PASSTHROUGH_RSP_EVT: {
+ case ESP_AVRC_CT_PASSTHROUGH_RSP_EVT:
+ case ESP_AVRC_CT_CHANGE_NOTIFY_EVT: {
bt_app_work_dispatch(bt_av_hdl_avrc_evt, event, param, sizeof(esp_avrc_ct_cb_param_t), NULL);
break;
}
}
}
+static void bt_av_new_track()
+{
+ //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);
+}
+
+void bt_av_notify_evt_handler(uint8_t event_id, uint32_t event_parameter)
+{
+ switch (event_id) {
+ case ESP_AVRC_RN_TRACK_CHANGE:
+ bt_av_new_track();
+ break;
+ }
+}
+
static void bt_av_hdl_avrc_evt(uint16_t event, void *p_param)
{
ESP_LOGD(BT_AV_TAG, "%s evt %d", __func__, 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, feature 0x%x, [%02x:%02x:%02x:%02x:%02x:%02x]",
- rc->conn_stat.connected, rc->conn_stat.feat_mask, bda[0], bda[1], bda[2], bda[3], bda[4], bda[5]);
+ rc->conn_stat.connected, rc->conn_stat.feat_mask, bda[0], bda[1], bda[2], bda[3], bda[4], bda[5]);
+
+ if (rc->conn_stat.connected) {
+ bt_av_new_track();
+ }
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);
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);
+ 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);
+ break;
+ }
default:
ESP_LOGE(BT_AV_TAG, "%s unhandled evt %d", __func__, event);
break;