--- /dev/null
+/*
+ * Open Adaptation Layer (OAL)
+ *
+ * Copyright (c) 2014-2015 Samsung Electronics Co., 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.
+ *
+ */
+
+#ifndef _OAL_AVRCP_HOST_H_
+#define _OAL_AVRCP_HOST_H_
+
+#include <glib.h>
+#include <sys/types.h>
+#include <oal-manager.h>
+#include "oal-event.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define OAL_MAX_ATTR_STR_LEN 255
+
+/* Maximum service name length */
+#define BT_AVRCP_NAME_LENGTH_MAX 35
+
+/**
+ * @brief AVRCP CMD send to remote BT device
+ *
+ * @see avrcp_send_cmd
+ */
+typedef enum {
+ AVRCP_DEVICE_OFF,
+ AVRCP_VOLUME_UP,
+ AVRCP_VOLUME_DOWN,
+ AVRCP_MUTE
+} avrcp_cmd;
+
+/**
+ * @brief Media's different properties type set to remote BT device
+ *
+ * @see avrcp_set_property
+ */
+typedef enum {
+ AVRCP_EQUALIZER = 0x01,
+ AVRCP_REPEAT,
+ AVRCP_SHUFFLE,
+ AVRCP_SCAN,
+ AVRCP_STATUS,
+ AVRCP_POSITION
+} oal_media_prop_type;
+
+/**
+ * @brief Player repeat status
+ *
+ * @see avrcp_set_property
+ */
+typedef enum {
+ OAL_PLAYER_VAL_OFF_REPEAT = 0x01,
+ OAL_PLAYER_VAL_SINGLE_REPEAT = 0x02,
+ OAL_PLAYER_VAL_ALL_REPEAT = 0x03,
+ OAL_PLAYER_VAL_GROUP_REPEAT = 0x04
+} oal_player_repeat_status;
+
+/**
+ * @brief Player shuffel mode
+ *
+ * @see avrcp_set_property
+ */
+typedef enum {
+ OAL_PLAYER_VAL_OFF_SHUFFLE = 0x01,
+ OAL_PLAYER_VAL_ALL_SHUFFLE = 0x02,
+ OAL_PLAYER_VAL_GROUP_SHUFFLE = 0x03
+} oal_player_shuffle_status;
+
+/**
+ * @brief Player play state
+ *
+ * @see avrcp_set_property
+ */
+typedef enum {
+ OAL_PLAYSTATE_STOPPED = 0x00, /* Stopped */
+ OAL_PLAYSTATE_PLAYING = 0x01, /* Playing */
+ OAL_PLAYSTATE_PAUSED = 0x02, /* Paused */
+ OAL_PLAYSTATE_FWD_SEEK = 0x03, /* Fwd Seek*/
+ OAL_PLAYSTATE_REV_SEEK = 0x04, /* Rev Seek*/
+ OAL_PLAYSTATE_ERROR = 0xFF, /* Error */
+} oal_play_status;
+
+/**
+ * @brief Media Track's attibutes set to remote BT device
+ *
+ * @see avrcp_set_track_info
+ */
+typedef struct {
+ char title[OAL_MAX_ATTR_STR_LEN];
+ char artist[OAL_MAX_ATTR_STR_LEN];
+ char album[OAL_MAX_ATTR_STR_LEN];
+ char genre[OAL_MAX_ATTR_STR_LEN];
+ unsigned int total_tracks;
+ unsigned int number;
+ unsigned int duration;
+} oal_media_metadata_attributes_t;
+
+/**
+ * @brief AVRCP Volume and mute
+ *
+ * @see OAL_EVENT_AVRCP_VOLUME_MUTE_CHANGED
+ */
+typedef struct {
+ gboolean mute_status ;
+ unsigned int volume;
+} oal_avrcp_volume_mute_t;
+
+typedef struct {
+ int avrcp_feature;
+ bt_address_t address;
+ char avrcp_serv_name[BT_AVRCP_NAME_LENGTH_MAX+1];
+ char avrcp_prov_name[BT_AVRCP_NAME_LENGTH_MAX+1];
+} remote_feature;
+
+/**
+ * @brief Enable AVRCP Feature
+ *
+ * @remarks AVRCP Feature (like play/pause, media information etc) will be enabled.
+ * @remarks Used in conjunction with a2dp_enable
+ *
+ * @return OAL_STATUS_SUCCESS on success, otherwise non-zero error value.
+ * @retval #OAL_STATUS_SUCCESS Successful
+ *
+ * @pre OAL API must be initialized with oal_bt_init().
+ *
+ * @see avrcp_disable()
+ * @see a2dp_enable()
+ */
+oal_status_t avrcp_enable(void);
+
+/**
+ * @brief Disable AVRCP Feature
+ *
+ * @remarks AVRCP Feature (like play/pause, media information etc) will be disabled.
+ * @remarks Used in conjunction with a2dp_disable.
+ *
+ * @return OAL_STATUS_SUCCESS on success, otherwise a non-zero error value.
+ * @retval #OAL_STATUS_SUCCESS Successful
+ *
+ * @pre AVRCP should be enabled with avrcp_enable().
+ *
+ * @see avrcp_enable()
+ * @see a2dp_disable()
+ */
+oal_status_t avrcp_disable(void);
+
+/**
+ * @brief Initiate a connection with AVRCP controller device
+ *
+ * @details Result will be notified through an OAL event
+ *
+ * @return OAL_STATUS_SUCCESS on success, otherwise a non-zero error value.
+ * @retval #OAL_STATUS_SUCCESS Successful
+ *
+ * @pre BT Audio should be enabled with avrcp_tg_enable().
+ *
+ * @see OAL_EVENT_AVRCP_CONNECTED
+ */
+oal_status_t avrcp_tg_connect(bt_address_t *rem_addr);
+
+
+/**
+ * @brief Remove a connection with AVRCP controller device
+ *
+ * @details Result will be notified through an OAL event
+ *
+ * @return OAL_STATUS_SUCCESS on success, otherwise a non-zero error value.
+ * @retval #OAL_STATUS_SUCCESS Successful
+ *
+ * @pre BT Audio should be connected with a BT Sound device.
+ *
+ * @see OAL_EVENT_AVRCP_DISCONNECTED
+ */
+oal_status_t avrcp_tg_disconnect(bt_address_t *rem_addr);
+
+/**
+ * @brief Set the AVRCP property to BT audio remote device
+ *
+ * @details Used to set the different type of AVRCP property.
+ *
+ * @return OAL_STATUS_SUCCESS on success, otherwise a non-zero error value.
+ * @retval #OAL_STATUS_SUCCESS Successful
+ *
+ * @pre AVRCP should be enabled with avrcp_enable().
+ *
+ * @see avrcp_enable()
+ */
+oal_status_t avrcp_set_property(int type, unsigned int value);
+
+/**
+ * @brief Set the media track info to BT audio remote device
+ *
+ * @details Used to set the different parameters(title, album, genre etc) of media track.
+ *
+ * @return OAL_STATUS_SUCCESS on success, otherwise a non-zero error value.
+ * @retval #OAL_STATUS_SUCCESS Successful
+ *
+ * @pre AVRCP should be enabled with avrcp_enable().
+ *
+ * @see avrcp_enable()
+ */
+oal_status_t avrcp_set_track_info(oal_media_metadata_attributes_t *meta_data);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /*_OAL_AVRCP_HOST_H_*/
--- /dev/null
+/*
+ * Open Adaptation Layer (OAL)
+ *
+ * Copyright (c) 2014-2015 Samsung Electronics Co., 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.
+ *
+ */
+
+#include <dlog.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "bluetooth.h"
+#include "bt_rc.h"
+
+#include "oal-event.h"
+#include "oal-internal.h"
+#include "oal-common.h"
+#include "oal-manager.h"
+#include "oal-avrcp-tg.h"
+
+#define NUM_MEDIA_ATTR_MAX (BTRC_MEDIA_ATTR_PLAYING_TIME)
+
+#define CHECK_OAL_AVRCP_ENABLED() \
+ do { \
+ if (avrcp_api == NULL) { \
+ BT_ERR("AVRCP Not Enabled"); \
+ return OAL_STATUS_NOT_READY; \
+ } \
+ } while (0)
+
+static void remove_pos_timer();
+
+#ifdef TIZEN_BT_HAL
+static void cb_connection_state(bt_bdaddr_t* bd_addr, btrc_connection_state_t state);
+#endif
+
+static void cb_avrcp_remote_features(bt_bdaddr_t* bd_addr, btrc_remote_features_t features);
+
+static void cb_avrcp_get_play_status();
+static void cb_avrcp_set_player_app_value(btrc_player_settings_t *p_vals, bt_bdaddr_t *bd_addr);
+static void cb_avrcp_get_element_attr(uint8_t num_attr, btrc_media_attr_t *p_attrs, bt_bdaddr_t *bd_addr);
+static void cb_avrcp_register_notification(btrc_event_id_t event_id, uint32_t param, bt_bdaddr_t *bd_addr);
+static void cb_avrcp_volume_change(uint8_t volume, uint8_t ctype, bt_bdaddr_t *bd_addr);
+static void cb_avrcp_passthrough_command(int id, int pressed, bt_bdaddr_t *bd_addr);
+
+typedef struct {
+ int play_status:1;
+ int track_change:1;
+ int track_end:1;
+ int track_start:1;
+ int pos_change:1;
+ int setting_change:1;
+ int interval;
+} notif_t;
+
+typedef struct {
+ int equalizer;
+ btrc_player_repeat_val_t repeat;
+ btrc_player_shuffle_val_t shuffle;
+ int scan;
+ btrc_play_status_t status;
+ unsigned int volume;
+} player_settings_t;
+
+typedef struct {
+ uint32_t song_pos;
+ uint32_t playing_time;
+ uint8_t title[BTRC_MAX_ATTR_STR_LEN];
+ uint8_t artist[BTRC_MAX_ATTR_STR_LEN];
+ uint8_t album[BTRC_MAX_ATTR_STR_LEN];
+ uint8_t genre[BTRC_MAX_ATTR_STR_LEN];
+ unsigned int num_tracks;
+ uint32_t cur_track;
+} track_info_t;
+
+typedef enum {
+ STATUS_STOPPED = 0x00,
+ STATUS_PLAYING,
+ STATUS_PAUSED,
+ STATUS_FORWARD_SEEK,
+ STATUS_REVERSE_SEEK,
+ STATUS_ERROR,
+ STATUS_INVALID
+} media_player_status;
+
+static track_info_t track_info;
+static player_settings_t player_setting;
+
+static guint send_pos_timer = 0;
+
+static notif_t registered_notifications;
+static const btrc_interface_t *avrcp_api;
+
+static btrc_callbacks_t sBluetoothAvrcpCallbacks = {
+ sizeof(sBluetoothAvrcpCallbacks),
+#ifdef TIZEN_BT_HAL
+ cb_connection_state,
+#endif
+ cb_avrcp_remote_features,
+ cb_avrcp_get_play_status,
+ NULL, /* cb_avrcp_list_player_app_attr, */
+ NULL, /* cb_avrcp_list_player_app_values, */
+ NULL, /* cb_avrcp_get_player_app_value, */
+ NULL, /* cb_avrcp_get_player_app_attrs_text, */
+ NULL, /* cb_avrcp_get_player_app_values_text, */
+ cb_avrcp_set_player_app_value,
+ cb_avrcp_get_element_attr,
+ cb_avrcp_register_notification,
+ cb_avrcp_volume_change,
+ cb_avrcp_passthrough_command,
+ NULL, /* cb_avrcp_set_addressed_player, */
+ NULL, /* cb_avrcp_set_browsed_player, */
+ NULL, /* cb_avrcp_get_folder_items, */
+ NULL, /* cb_avrcp_change_path, */
+ NULL, /* cb_avrcp_get_item_attr, */
+ NULL, /* cb_avrcp_play_item, */
+ NULL, /* cb_avrcp_get_total_num_of_items, */
+ NULL, /* cb_avrcp_search, */
+ NULL, /* cb_avrcp_add_to_now_playing, */
+};
+
+static void send_pos_changed(void)
+{
+ BT_DBG("Pos changed");
+
+ if (avrcp_api == NULL) {
+ BT_ERR("AVRCP Not Enabled");
+ return ;
+ }
+
+ if(registered_notifications.pos_change) {
+ int ret;
+ btrc_register_notification_t response;
+
+ BT_DBG("Song Pos: %d", track_info.song_pos);
+ response.song_pos = track_info.song_pos;
+ ret = avrcp_api->register_notification_rsp(
+ BTRC_EVT_PLAY_POS_CHANGED, BTRC_NOTIFICATION_TYPE_CHANGED, &response);
+
+ if(ret != BT_STATUS_SUCCESS)
+ BT_ERR("Notif send failed: %s", status2string(ret));
+ }
+}
+
+static gboolean send_pos_timeout(gpointer param)
+{
+ BT_DBG("pos timeout");
+
+ if (!registered_notifications.pos_change)
+ return FALSE;
+
+ send_pos_changed();
+
+ if (player_setting.status != BTRC_PLAYSTATE_PLAYING &&
+ player_setting.status != BTRC_PLAYSTATE_FWD_SEEK &&
+ player_setting.status != BTRC_PLAYSTATE_REV_SEEK)
+ return FALSE;
+
+ return TRUE;
+}
+
+static void remove_pos_timer()
+{
+
+ if(send_pos_timer > 0) {
+ g_source_remove(send_pos_timer);
+ send_pos_timer = 0;
+ }
+}
+
+static void send_track_boundary_reached(void)
+{
+ int ret = BT_STATUS_SUCCESS;
+
+ if (avrcp_api == NULL) {
+ BT_ERR("AVRCP Not Enabled");
+ return;
+ }
+
+ if (track_info.song_pos == 0 &&
+ registered_notifications.track_start)
+ ret = avrcp_api->register_notification_rsp(
+ BTRC_EVT_TRACK_REACHED_START, BTRC_NOTIFICATION_TYPE_CHANGED, NULL);
+ else if ((track_info.playing_time == track_info.song_pos) &&
+ registered_notifications.track_end) {
+ ret = avrcp_api->register_notification_rsp(
+ BTRC_EVT_TRACK_REACHED_END, BTRC_NOTIFICATION_TYPE_CHANGED, NULL);
+ }
+
+ if(ret != BT_STATUS_SUCCESS)
+ BT_ERR("Notif send failed: %s", status2string(ret));
+}
+
+gboolean avrcp_tg_enable_state(void)
+{
+ if(avrcp_api == NULL)
+ return FALSE;
+ else
+ return TRUE;
+}
+
+oal_status_t avrcp_enable(void)
+{
+ const bt_interface_t * blued_api = NULL;
+ int ret;
+
+ API_TRACE();
+
+ /*TODO: Need to check below logic */
+#if 0
+ if(a2dp_sink_enable_state() == TRUE || avrcp_ct_enable_state() == TRUE) {
+ BT_ERR("AVRCP_CT Role Enabled, cannot enable AVRCP_TG Role");
+ return OAL_STATUS_BUSY;
+ }
+#endif
+
+ blued_api = adapter_get_stack_interface();
+ if (blued_api == NULL) {
+ BT_ERR("Stack is not initialized");
+ return OAL_STATUS_NOT_READY;
+ }
+
+ if (avrcp_api) {
+ BT_WARN("avrcp tg Interface is already initialized...");
+ return OAL_STATUS_ALREADY_DONE;
+ }
+
+ avrcp_api = (const btrc_interface_t *)blued_api->get_profile_interface(BT_PROFILE_AV_RC_ID);
+ if (avrcp_api == NULL) {
+ BT_ERR("AVRCP interface failed");
+ return OAL_STATUS_INTERNAL_ERROR;
+ }
+
+ memset(®istered_notifications, 0x00, sizeof(registered_notifications));
+ memset(&track_info, 0x00, sizeof(track_info));
+ memset(&player_setting, 0, sizeof(player_settings_t));
+
+ /* Handle failure if return value is anything other than Success or Already done */
+ ret = avrcp_api->init(&sBluetoothAvrcpCallbacks);
+ if (ret != BT_STATUS_SUCCESS && ret != BT_STATUS_DONE) {
+ BT_ERR("AVRCP Init failed %s", status2string(ret));
+ avrcp_api->cleanup();
+ avrcp_api = NULL;
+ return convert_to_oal_status(ret);
+ }
+
+ return OAL_STATUS_SUCCESS;
+}
+
+oal_status_t avrcp_disable(void) {
+
+ API_TRACE();
+
+ CHECK_OAL_AVRCP_ENABLED();
+
+ remove_pos_timer();
+ avrcp_api->cleanup();
+ avrcp_api = NULL;
+
+ return OAL_STATUS_SUCCESS;
+}
+
+void avrcp_tg_cleanup(void) {
+
+ BT_DBG();
+
+ remove_pos_timer();
+ avrcp_api = NULL;
+}
+
+oal_status_t avrcp_tg_connect(bt_address_t *rem_addr)
+{
+ int result = OAL_STATUS_SUCCESS;
+ bt_status_t status;
+ bdstr_t bdstr;
+
+ API_TRACE();
+
+ CHECK_OAL_AVRCP_ENABLED();
+ OAL_CHECK_PARAMETER(rem_addr, return);
+
+ BT_INFO("BT Audio Address: %s", bdt_bd2str(rem_addr, &bdstr));
+
+ status = avrcp_api->connect((bt_bdaddr_t *)rem_addr);
+ if((status != BT_STATUS_SUCCESS) && (status != BT_STATUS_DONE)) {
+ BT_ERR("Connection could not be established, err: %s", status2string(status));;
+ result = convert_to_oal_status(status);
+ }
+
+ return result;
+}
+
+oal_status_t avrcp_tg_disconnect( bt_address_t *rem_addr)
+{
+ int result = OAL_STATUS_SUCCESS;
+ bdstr_t bdstr;
+ bt_status_t status;
+
+ API_TRACE();
+
+ CHECK_OAL_AVRCP_ENABLED();
+ OAL_CHECK_PARAMETER(rem_addr, return);
+
+ BT_INFO("BT Audio Address: %s", bdt_bd2str(rem_addr, &bdstr));
+
+ status = avrcp_api->disconnect((bt_bdaddr_t *)rem_addr);
+ if((status != BT_STATUS_SUCCESS) && (status != BT_STATUS_DONE)) {
+ BT_ERR("OAL, Disconnection failed err: %s", status2string(status));
+ result = convert_to_oal_status(status);
+ }
+
+ return result;
+}
+
+oal_status_t avrcp_set_track_info(oal_media_metadata_attributes_t *meta_data)
+{
+ API_TRACE();
+
+ CHECK_OAL_AVRCP_ENABLED();
+
+ retv_if(meta_data == NULL, OAL_STATUS_INTERNAL_ERROR);
+
+ /********* Update media attribs **********/
+ g_strlcpy((char*)track_info.title, meta_data->title, BTRC_MAX_ATTR_STR_LEN);
+ g_strlcpy((char*)track_info.artist, meta_data->artist, BTRC_MAX_ATTR_STR_LEN);
+ g_strlcpy((char*)track_info.album, meta_data->album, BTRC_MAX_ATTR_STR_LEN);
+ g_strlcpy((char*)track_info.genre, meta_data->genre, BTRC_MAX_ATTR_STR_LEN );
+
+ track_info.num_tracks = meta_data->total_tracks;
+ track_info.playing_time = meta_data->duration;
+
+ if (registered_notifications.track_change &&
+ track_info.cur_track != meta_data->number) {
+ int ret;
+ btrc_register_notification_t response;
+
+ track_info.cur_track = meta_data->number;
+
+ /* Send Track Change notification */
+ memcpy(&response.track, &track_info.cur_track, BTRC_UID_SIZE);
+ ret = avrcp_api->register_notification_rsp(
+ BTRC_EVT_TRACK_CHANGE, BTRC_NOTIFICATION_TYPE_CHANGED, &response);
+ if(ret != BT_STATUS_SUCCESS)
+ BT_ERR("Notif send failed: %s", status2string(ret));
+
+ send_pos_changed();
+ }
+
+ track_info.cur_track = meta_data->number;
+ return OAL_STATUS_SUCCESS;
+}
+
+oal_status_t avrcp_set_property(int type, unsigned int value)
+{
+ btrc_register_notification_t response;
+ int ret;
+
+ API_TRACE("type: %d, value: %d", type, value);
+ CHECK_OAL_AVRCP_ENABLED();
+
+ switch (type) {
+ case AVRCP_EQUALIZER: {
+ if (value == player_setting.equalizer &&
+ !registered_notifications.setting_change)
+ break;
+
+ response.player_setting.num_attr = 1;
+ response.player_setting.attr_ids[0] = BTRC_PLAYER_ATTR_EQUALIZER;
+ response.player_setting.attr_values[0] = value;
+ ret = avrcp_api->register_notification_rsp(BTRC_EVT_APP_SETTINGS_CHANGED,
+ BTRC_NOTIFICATION_TYPE_CHANGED, &response);
+ if(ret != BT_STATUS_SUCCESS)
+ BT_ERR("Notif send failed: %s", status2string(ret));
+
+ player_setting.equalizer = value;
+ break;
+ }
+ case AVRCP_REPEAT: {
+ if (value == player_setting.repeat &&
+ !registered_notifications.setting_change)
+ break;
+
+ response.player_setting.num_attr = 1;
+ response.player_setting.attr_ids[0] = BTRC_PLAYER_ATTR_REPEAT;
+ response.player_setting.attr_values[0] = value;
+ ret = avrcp_api->register_notification_rsp(BTRC_EVT_APP_SETTINGS_CHANGED,
+ BTRC_NOTIFICATION_TYPE_CHANGED, &response);
+ if(ret != BT_STATUS_SUCCESS)
+ BT_ERR("Notif send failed: %s", status2string(ret));
+
+ player_setting.repeat = value;
+ break;
+ }
+ case AVRCP_SHUFFLE: {
+ if (value == player_setting.shuffle &&
+ !registered_notifications.setting_change)
+ break;
+
+ response.player_setting.num_attr = 1;
+ response.player_setting.attr_ids[0] = BTRC_PLAYER_ATTR_SHUFFLE;
+ response.player_setting.attr_values[0] = value;
+ ret = avrcp_api->register_notification_rsp(BTRC_EVT_APP_SETTINGS_CHANGED,
+ BTRC_NOTIFICATION_TYPE_CHANGED, &response);
+ if(ret != BT_STATUS_SUCCESS)
+ BT_ERR("Notif send failed: %s", status2string(ret));
+
+ player_setting.shuffle = value;
+ break;
+ }
+ case AVRCP_SCAN: {
+ if (value == player_setting.scan &&
+ !registered_notifications.setting_change)
+ break;
+
+ response.player_setting.num_attr = 1;
+ response.player_setting.attr_ids[0] = BTRC_PLAYER_ATTR_SCAN;
+ response.player_setting.attr_values[0] = value;
+ ret = avrcp_api->register_notification_rsp(BTRC_EVT_APP_SETTINGS_CHANGED,
+ BTRC_NOTIFICATION_TYPE_CHANGED, &response);
+ if(ret != BT_STATUS_SUCCESS)
+ BT_ERR("Notif send failed: %s", status2string(ret));
+
+ player_setting.scan = value;
+ break;
+ }
+ case AVRCP_STATUS:
+ if(value != player_setting.status) {
+ btrc_play_status_t play_status = player_setting.status;
+
+ player_setting.status = (value == STATUS_ERROR) ? BTRC_PLAYSTATE_ERROR : value;
+
+ if (registered_notifications.play_status) {
+ gboolean is_timer = FALSE;
+
+ response.play_status = player_setting.status;
+ ret = avrcp_api->register_notification_rsp(BTRC_EVT_PLAY_STATUS_CHANGED,
+ BTRC_NOTIFICATION_TYPE_CHANGED, &response);
+ if(ret != BT_STATUS_SUCCESS)
+ BT_ERR("Notif send failed: %s", status2string(ret));
+
+ /* Check if old and new status value are changed from NOT PLAYING to PLAYING */
+ switch (play_status) {
+ case BTRC_PLAYSTATE_ERROR: /* Intentional fall-through */
+ case BTRC_PLAYSTATE_STOPPED: /* Intentional fall-through */
+ case BTRC_PLAYSTATE_PAUSED: /* Intentional fall-through */
+ if (STATUS_PLAYING == value ||
+ STATUS_REVERSE_SEEK == value ||
+ STATUS_FORWARD_SEEK == value) {
+ BT_INFO("Play status changed from stopped to playing");
+ is_timer = TRUE;
+ }
+ break;
+ default:
+ is_timer = FALSE;
+ }
+
+ if (is_timer) {
+ BT_DBG("Player is playing mode, start sending pos change notifications");
+ remove_pos_timer();
+ send_pos_timer = g_timeout_add(registered_notifications.interval * 1000,
+ send_pos_timeout, NULL);
+ }
+ }
+ }
+ break;
+ case AVRCP_POSITION:
+ if(value != track_info.song_pos) {
+ track_info.song_pos = value;
+ send_pos_changed();
+ send_track_boundary_reached();
+ }
+ break;
+ default:
+ BT_ERR("Invalid Type\n");
+ return OAL_STATUS_INTERNAL_ERROR;
+ }
+
+ return OAL_STATUS_SUCCESS;
+}
+
+#ifdef TIZEN_BT_HAL
+static void cb_connection_state(bt_bdaddr_t* bd_addr, btrc_connection_state_t state)
+{
+ bt_address_t * event_data = NULL;
+ int event;
+
+ BT_DBG("%d", state);
+
+ event_data = g_new0(bt_address_t, 1);
+ memcpy(event_data->addr, bd_addr->address, BT_ADDRESS_BYTES_NUM);
+
+ switch(state) {
+ case BTRC_CONNECTION_STATE_DISCONNECTED:
+ event = OAL_EVENT_AVRCP_DISCONNECTED;
+ break;
+ case BTRC_CONNECTION_STATE_CONNECTED:
+ event = OAL_EVENT_AVRCP_CONNECTED;
+ break;
+ default:
+ BT_ERR("Unhandled Connection state %d", state);
+ return;
+ }
+
+ send_event_bda_trace(event, event_data, sizeof(bt_address_t), (bt_address_t*)bd_addr);
+
+}
+#endif
+
+static void cb_avrcp_remote_features(bt_bdaddr_t* bd_addr, btrc_remote_features_t features)
+{
+ remote_feature *avrcp_rem_feature = g_new0(remote_feature, 1);
+
+ /* Reset variables */
+ memset(®istered_notifications, 0, sizeof(notif_t));
+ memset(&track_info, 0, sizeof(track_info_t));
+ memset(&player_setting, 0, sizeof(player_settings_t));
+ player_setting.volume= 0xFFFFFFFF/2;
+
+ /* TODO: need to check if this feature mask is useful */
+ BT_INFO("Remore features Mask: 0x%x", features);
+
+ avrcp_rem_feature->avrcp_feature = features;
+ memcpy(avrcp_rem_feature->address.addr, bd_addr->address, 6);
+
+ send_event_bda_trace(OAL_EVENT_AVRCP_REMOTE_FEATURES,
+ avrcp_rem_feature, sizeof(remote_feature), (bt_address_t *)bd_addr);
+}
+
+static void cb_avrcp_get_play_status(bt_bdaddr_t *bd_addr)
+{
+ int ret;
+
+ BT_DBG("");
+
+ if (avrcp_api == NULL) {
+ BT_ERR("AVRCP Not Enabled");
+ return ;
+ }
+
+ ret = avrcp_api->get_play_status_rsp(bd_addr, player_setting.status,
+ track_info.playing_time, track_info.song_pos);
+ if(ret != BT_STATUS_SUCCESS)
+ BT_ERR("Play Status send failed: %s", status2string(ret));
+}
+
+static void cb_avrcp_set_player_app_value(btrc_player_settings_t *p_vals, bt_bdaddr_t *bd_addr)
+{
+ int i;
+ uint8_t *value ;
+ oal_event_t event;
+
+ BT_DBG("");
+
+ for (i = 0; i < p_vals->num_attr; i++) {
+ value = g_new0(uint8_t, 1);
+ event = 0xffff;
+
+ switch (p_vals->attr_ids[i]) {
+ case BTRC_PLAYER_ATTR_EQUALIZER:
+ event = OAL_EVENT_AVRCP_SETTING_EQUALIZER_STATUS;
+ break;
+ case BTRC_PLAYER_ATTR_REPEAT:
+ event = OAL_EVENT_AVRCP_SETTING_REPEAT_STATUS;
+ break;
+ case BTRC_PLAYER_ATTR_SHUFFLE:
+ event = OAL_EVENT_AVRCP_SETTING_SHUFFLE_STATUS;
+ break;
+ case BTRC_PLAYER_ATTR_SCAN:
+ event = OAL_EVENT_AVRCP_SETTING_SCAN_STATUS;
+ break;
+ default:
+ BT_ERR("Inavlid attr id= %d", p_vals->attr_ids[i]);
+ g_free(value);
+ }
+
+ if (event != 0xffff) {
+ *value = p_vals->attr_values[i];
+ send_event(event, value, sizeof(*value));
+ }
+ }
+}
+
+static void cb_avrcp_get_element_attr(uint8_t num_attr, btrc_media_attr_t *p_attrs, bt_bdaddr_t *bd_addr)
+{
+ btrc_element_attr_val_t *p_attrs_vals;
+ int ret;
+ int i;
+
+ if (avrcp_api == NULL) {
+ BT_ERR("AVRCP Not Enabled");
+ return;
+ }
+
+ if (p_attrs == NULL) {
+ BT_ERR("p_attrs is NULL");
+ return;
+ }
+
+ BT_DBG("num_attr: %d", num_attr);
+ p_attrs_vals = g_malloc0(num_attr * sizeof(btrc_element_attr_val_t));
+
+ for (i = 0; i < num_attr; i++) {
+ p_attrs_vals[i].attr_id = p_attrs[i];
+
+ switch (p_attrs[i]) {
+ case BTRC_MEDIA_ATTR_TITLE:
+ g_strlcpy((char*)p_attrs_vals[i].text,
+ (const char *)track_info.title, BTRC_MAX_ATTR_STR_LEN);
+ break;
+ case BTRC_MEDIA_ATTR_ARTIST:
+ g_strlcpy((char*)p_attrs_vals[i].text,
+ (const char *)track_info.artist, BTRC_MAX_ATTR_STR_LEN);
+ break;
+ case BTRC_MEDIA_ATTR_ALBUM:
+ g_strlcpy((char*)p_attrs_vals[i].text,
+ (const char *)track_info.album, BTRC_MAX_ATTR_STR_LEN);
+ break;
+ case BTRC_MEDIA_ATTR_TRACK_NUM:
+ snprintf((char*)p_attrs_vals[i].text, BTRC_MAX_ATTR_STR_LEN, "%u", track_info.cur_track);
+ break;
+ case BTRC_MEDIA_ATTR_NUM_TRACKS:
+ snprintf((char*)p_attrs_vals[i].text, BTRC_MAX_ATTR_STR_LEN, "%u", track_info.num_tracks);
+ break;
+ case BTRC_MEDIA_ATTR_GENRE:
+ g_strlcpy((char*)p_attrs_vals[i].text,
+ (const char *)track_info.genre, BTRC_MAX_ATTR_STR_LEN);
+ break;
+ case BTRC_MEDIA_ATTR_PLAYING_TIME:
+ snprintf((char*)p_attrs_vals[i].text, BTRC_MAX_ATTR_STR_LEN, "%u", track_info.playing_time);
+ break;
+ default:
+ BT_ERR("Inavlid attr id= %d", p_attrs[i]);
+ }
+ }
+
+ ret = avrcp_api->get_element_attr_rsp(bd_addr, num_attr, p_attrs_vals);
+ if(ret != BT_STATUS_SUCCESS)
+ BT_ERR("Element attr send failed: %s", status2string(ret));
+
+ g_free(p_attrs_vals);
+}
+
+static void cb_avrcp_register_notification(btrc_event_id_t event_id, uint32_t param, bt_bdaddr_t *bd_addr)
+{
+ btrc_register_notification_t response;
+ int ret;
+
+ BT_DBG("event_id: 0x%x", event_id);
+
+ if (avrcp_api == NULL) {
+ BT_ERR("AVRCP Not Enabled");
+ return ;
+ }
+
+ switch (event_id) {
+ case BTRC_EVT_PLAY_STATUS_CHANGED:
+ registered_notifications.play_status = 1;
+ response.play_status = player_setting.status;
+ ret = avrcp_api->register_notification_rsp(
+ BTRC_EVT_PLAY_STATUS_CHANGED, BTRC_NOTIFICATION_TYPE_INTERIM, &response);
+ break;
+ case BTRC_EVT_TRACK_CHANGE:
+ registered_notifications.track_change = 1;
+ memcpy(response.track, &track_info.cur_track, BTRC_UID_SIZE);
+ ret = avrcp_api->register_notification_rsp(
+ BTRC_EVT_TRACK_CHANGE, BTRC_NOTIFICATION_TYPE_INTERIM, &response);
+ break;
+ case BTRC_EVT_TRACK_REACHED_END:
+ registered_notifications.track_end = 1;
+ ret = avrcp_api->register_notification_rsp(
+ BTRC_EVT_TRACK_REACHED_END, BTRC_NOTIFICATION_TYPE_INTERIM, NULL);
+ break;
+ case BTRC_EVT_TRACK_REACHED_START:
+ registered_notifications.track_start = 1;
+ ret = avrcp_api->register_notification_rsp(
+ BTRC_EVT_TRACK_REACHED_START, BTRC_NOTIFICATION_TYPE_INTERIM, NULL);
+ break;
+ case BTRC_EVT_PLAY_POS_CHANGED:
+ registered_notifications.pos_change = 1;
+ registered_notifications.interval = param;
+ BT_DBG("Pos will be notified every %d secs", param);
+ /*
+ * start a timer with value of interval and send rsp on each timer fire.
+ * Other then this in below situations also send the response:
+ * - Change in Play status
+ * - Change in current track
+ * - Reach end or beginning of track
+ */
+ response.song_pos = track_info.song_pos;
+ ret = avrcp_api->register_notification_rsp(
+ BTRC_EVT_PLAY_POS_CHANGED, BTRC_NOTIFICATION_TYPE_INTERIM, &response);
+ remove_pos_timer();
+ send_pos_timer = g_timeout_add(param * 1000, send_pos_timeout, NULL);
+ break;
+ case BTRC_EVT_APP_SETTINGS_CHANGED:
+ registered_notifications.setting_change = 1;
+ ret = avrcp_api->register_notification_rsp(
+ BTRC_EVT_APP_SETTINGS_CHANGED, BTRC_NOTIFICATION_TYPE_INTERIM, &response);
+ break;
+ default:
+ BT_ERR("Invalid event id: 0x%x", event_id);
+ ret = BT_STATUS_FAIL;
+ }
+
+ if(ret != BT_STATUS_SUCCESS)
+ BT_ERR("Notif send failed: %s", status2string(ret));
+}
+
+static void cb_avrcp_volume_change(uint8_t volume, uint8_t ctype, bt_bdaddr_t *bd_addr)
+{
+
+ oal_avrcp_volume_mute_t *avrcp_volume_mute = g_new0(oal_avrcp_volume_mute_t, 1);
+
+ BT_INFO("volume: %d, ctype: %d", volume, ctype);
+ player_setting.volume = volume;
+
+ avrcp_volume_mute->volume = volume;
+ if (0 == volume)
+ avrcp_volume_mute->mute_status = TRUE;
+ else
+ avrcp_volume_mute->mute_status = FALSE;
+
+ send_event(OAL_EVENT_AVRCP_VOLUME_MUTE_CHANGED,
+ avrcp_volume_mute, sizeof(oal_avrcp_volume_mute_t));
+}
+
+static void cb_avrcp_passthrough_command(int id, int key_state, bt_bdaddr_t *bd_addr)
+{
+ BT_DBG("id: %d, key_state: %d", id, key_state);
+ //TODO: need to check
+}