#define AVRCP_REQUEST_CONTINUING 0x40
#define AVRCP_ABORT_CONTINUING 0x41
#define AVRCP_SET_ABSOLUTE_VOLUME 0x50
+#define AVRCP_SET_ADDRESSED_PLAYER 0x60
#define AVRCP_SET_BROWSED_PLAYER 0x70
#define AVRCP_GET_FOLDER_ITEMS 0x71
#define AVRCP_CHANGE_PATH 0x72
#define AVRCP_GET_ITEM_ATTRIBUTES 0x73
#define AVRCP_PLAY_ITEM 0x74
+#define AVRCP_GET_TOTAL_NUMBER_OF_ITEMS 0x75
#define AVRCP_SEARCH 0x80
#define AVRCP_ADD_TO_NOW_PLAYING 0x90
#define AVRCP_GENERAL_REJECT 0xA0
#define AVRCP_CHARSET_UTF8 106
#define AVRCP_BROWSING_TIMEOUT 1
+#ifdef __TIZEN_PATCH__
+#define AVRCP_CT_VERSION 0x0103
+#define AVRCP_TG_VERSION 0x0103
+#else
+#define AVRCP_CT_VERSION 0x0106
+#define AVRCP_TG_VERSION 0x0105
+#endif
+#define AVRCP_SCOPE_MEDIA_PLAYER_LIST 0x00
+#define AVRCP_SCOPE_MEDIA_PLAYER_VFS 0x01
+#define AVRCP_SCOPE_SEARCH 0x02
+#define AVRCP_SCOPE_NOW_PLAYING 0x03
#if __BYTE_ORDER == __LITTLE_ENDIAN
} __attribute__ ((packed));
#define AVRCP_BROWSING_HEADER_LENGTH 3
+struct get_folder_items_rsp {
+ uint8_t status;
+ uint16_t uid_counter;
+ uint16_t num_items;
+ uint8_t data[0];
+} __attribute__ ((packed));
+
+struct folder_item {
+ uint8_t type;
+ uint16_t len;
+ uint8_t data[0];
+} __attribute__ ((packed));
+
+struct player_item {
+ uint16_t player_id;
+ uint8_t type;
+ uint32_t subtype;
+ uint8_t status;
+ uint8_t features[16];
+ uint16_t charset;
+ uint16_t namelen;
+ char name[0];
+} __attribute__ ((packed));
+
struct avrcp_server {
struct btd_adapter *adapter;
uint32_t tg_record_id;
uint64_t uid;
uint16_t uid_counter;
bool browsed;
+ bool addressed;
uint8_t *features;
char *path;
+ guint changed_id;
struct pending_list_items *p;
char *change_path;
uint8_t transaction;
uint8_t transaction_events[AVRCP_EVENT_LAST + 1];
struct pending_pdu *pending_pdu;
+#ifdef __TIZEN_PATCH__
+ uint32_t playback_status_id;
+#endif
};
struct passthrough_handler {
};
static GSList *servers = NULL;
-#if defined(SUPPORT_AVRCP_TARGET) || defined(SUPPORT_AVRCP_CONTROL)
static unsigned int avctp_id = 0;
-#endif
-#ifdef SUPPORT_AVRCP_TARGET
#ifdef __TIZEN_PATCH__
+#ifdef SUPPORT_AVRCP_TARGET
static uint16_t adapter_avrcp_tg_ver = 0;
#endif
-#endif
-
#ifdef SUPPORT_AVRCP_CONTROL
-#ifdef __TIZEN_PATCH__
static uint16_t adapter_avrcp_ct_ver = 0;
#endif
#endif
+/* Default feature bit mask for media player as per avctp.c:key_map */
+static const uint8_t features[16] = {
+ 0xF8, 0xBF, 0xFF, 0xBF, 0x1F,
+ 0xFB, 0x3F, 0x60, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00 };
+
/* Company IDs supported by this device */
static uint32_t company_ids[] = {
IEEEID_BTSIG,
sdp_data_t *psm[2], *version, *features;
uint16_t lp = AVCTP_CONTROL_PSM, ap = AVCTP_BROWSING_PSM;
#ifdef __TIZEN_PATCH__
- uint16_t avrcp_ver = 0x0103, avctp_ver = 0x0104;
+ uint16_t avctp_ver = 0x0104;
uint16_t feat = 0;
#ifdef ENABLE_AVRCP_CATEGORY1
feat = AVRCP_FEATURE_CATEGORY_1;
feat = feat | AVRCP_FEATURE_CATEGORY_2;
#endif
#else
- uint16_t avrcp_ver = 0x0105, avctp_ver = 0x0103;
+ uint16_t avctp_ver = 0x0103;
uint16_t feat = ( AVRCP_FEATURE_CATEGORY_1 |
AVRCP_FEATURE_CATEGORY_2 |
AVRCP_FEATURE_CATEGORY_3 |
/* Bluetooth Profile Descriptor List */
sdp_uuid16_create(&profile[0].uuid, AV_REMOTE_PROFILE_ID);
- profile[0].version = avrcp_ver;
+ profile[0].version = AVRCP_CT_VERSION;
#ifdef __TIZEN_PATCH__
- adapter_avrcp_ct_ver = avrcp_ver;
+ adapter_avrcp_ct_ver = AVRCP_CT_VERSION;
#endif
pfseq = sdp_list_append(NULL, &profile[0]);
sdp_set_profile_descs(record, pfseq);
uint16_t lp = AVCTP_CONTROL_PSM;
uint16_t lp_browsing = AVCTP_BROWSING_PSM;
#ifdef __TIZEN_PATCH__
- uint16_t avrcp_ver = 0x0103, avctp_ver = 0x0104;
+ uint16_t avctp_ver = 0x0104;
uint16_t feat = 0;
#ifdef ENABLE_AVRCP_CATEGORY1
feat = AVRCP_FEATURE_CATEGORY_1 |
feat = feat | AVRCP_FEATURE_CATEGORY_2;
#endif
#else
- uint16_t avrcp_ver = 0x0104, avctp_ver = 0x0103;
+ uint16_t avctp_ver = 0x0103;
uint16_t feat = ( AVRCP_FEATURE_CATEGORY_1 |
AVRCP_FEATURE_CATEGORY_2 |
AVRCP_FEATURE_CATEGORY_3 |
/* Bluetooth Profile Descriptor List */
sdp_uuid16_create(&profile[0].uuid, AV_REMOTE_PROFILE_ID);
- profile[0].version = avrcp_ver;
+ profile[0].version = AVRCP_TG_VERSION;
#ifdef __TIZEN_PATCH__
- adapter_avrcp_tg_ver = avrcp_ver;
+ adapter_avrcp_tg_ver = AVRCP_TG_VERSION;
#endif
pfseq = sdp_list_append(NULL, &profile[0]);
sdp_set_profile_descs(record, pfseq);
{
uint8_t buf[AVRCP_HEADER_LENGTH + 9];
struct avrcp_header *pdu = (void *) buf;
+ uint8_t code;
uint16_t size;
GSList *l;
int attr;
set_company_id(pdu->company_id, IEEEID_BTSIG);
pdu->pdu_id = AVRCP_REGISTER_NOTIFICATION;
- pdu->params[0] = id;
DBG("id=%u", id);
+ if (id != AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED && player->changed_id) {
+ code = AVC_CTYPE_REJECTED;
+ size = 1;
+ pdu->params[0] = AVRCP_STATUS_ADDRESSED_PLAYER_CHANGED;
+ goto done;
+ }
+
+ code = AVC_CTYPE_CHANGED;
+ pdu->params[0] = id;
+
switch (id) {
case AVRCP_EVENT_STATUS_CHANGED:
size = 2;
memcpy(&pdu->params[1], position_val, sizeof(uint32_t));
break;
#endif
+ case AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED:
+ size = 5;
+ memcpy(&pdu->params[1], &player->id, sizeof(uint16_t));
+ memcpy(&pdu->params[3], &player->uid_counter, sizeof(uint16_t));
+ break;
+ case AVRCP_EVENT_AVAILABLE_PLAYERS_CHANGED:
+ size = 1;
+ break;
default:
error("Unknown event %u", id);
return;
}
+done:
pdu->params_len = htons(size);
#ifdef __TIZEN_PATCH__
if (id == AVRCP_EVENT_PLAYBACK_POS_CHANGED &&
err = avctp_send_vendordep(session->conn,
session->transaction_events[id],
- AVC_CTYPE_CHANGED, AVC_SUBUNIT_PANEL,
+ code, AVC_SUBUNIT_PANEL,
buf, size + AVRCP_HEADER_LENGTH);
+
if (err < 0)
continue;
return play_status_to_val(value);
}
+static uint16_t player_get_id(struct avrcp_player *player)
+{
+ if (player == NULL)
+ return 0x0000;
+
+ return player->id;
+}
+
+static uint16_t player_get_uid_counter(struct avrcp_player *player)
+{
+ if (player == NULL)
+ return 0x0000;
+
+ return player->uid_counter;
+}
+
static uint8_t avrcp_handle_get_play_status(struct avrcp *session,
struct avrcp_header *pdu,
uint8_t transaction)
{ },
};
-#if defined(SUPPORT_AVRCP_TARGET) || defined(SUPPORT_AVRCP_CONTROL)
static bool handle_passthrough(struct avctp *conn, uint8_t op, bool pressed,
void *user_data)
{
return handler->func(session);
}
-#endif
#ifdef __TIZEN_PATCH__
void avrcp_stop_position_timer(void)
pdu->params[len++] = val;
}
+ g_list_free(settings);
+
+ break;
+ case AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED:
+ len = 5;
+ bt_put_be16(player_get_id(player), &pdu->params[1]);
+ bt_put_be16(player_get_uid_counter(player), &pdu->params[3]);
+ break;
+ case AVRCP_EVENT_AVAILABLE_PLAYERS_CHANGED:
+ len = 1;
break;
case AVRCP_EVENT_VOLUME_CHANGED:
pdu->params[1] = media_transport_get_device_volume(dev);
struct avrcp_header *pdu,
uint8_t transaction)
{
-#ifndef __TIZEN_PATCH__
- struct avrcp_player *player = session->controller->player;
-#else
- struct avrcp_player *player = target_get_player(session);
-#endif
uint16_t len = ntohs(pdu->params_len);
uint8_t volume;
if (len != 1)
goto err;
- if (!player)
- goto err;
volume = pdu->params[0] & 0x7F;
return AVC_CTYPE_REJECTED;
}
+static struct avrcp_player *find_tg_player(struct avrcp *session, uint16_t id)
+{
+ struct avrcp_server *server = session->server;
+ GSList *l;
+
+ for (l = server->players; l; l = l->next) {
+ struct avrcp_player *player = l->data;
+
+ if (player->id == id)
+ return player;
+ }
+
+ return NULL;
+}
+
+static gboolean notify_addressed_player_changed(gpointer user_data)
+{
+ struct avrcp_player *player = user_data;
+ uint8_t events[6] = { AVRCP_EVENT_STATUS_CHANGED,
+ AVRCP_EVENT_TRACK_CHANGED,
+ AVRCP_EVENT_TRACK_REACHED_START,
+ AVRCP_EVENT_TRACK_REACHED_END,
+ AVRCP_EVENT_SETTINGS_CHANGED,
+ AVRCP_EVENT_PLAYBACK_POS_CHANGED
+ };
+ uint8_t i;
+
+ avrcp_player_event(player, AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED, NULL);
+
+ /*
+ * TG shall complete all player specific
+ * notifications with AV/C C-Type REJECTED
+ * with error code as Addressed Player Changed.
+ */
+ for (i = 0; i < sizeof(events); i++)
+ avrcp_player_event(player, events[i], NULL);
+
+ player->changed_id = 0;
+
+ return FALSE;
+}
+
+static uint8_t avrcp_handle_set_addressed_player(struct avrcp *session,
+ struct avrcp_header *pdu,
+ uint8_t transaction)
+{
+ struct avrcp_player *player;
+ uint16_t len = ntohs(pdu->params_len);
+ uint16_t player_id = 0;
+ uint8_t status;
+
+ if (len < 1) {
+ status = AVRCP_STATUS_INVALID_PARAM;
+ goto err;
+ }
+
+ player_id = bt_get_be16(&pdu->params[0]);
+ player = find_tg_player(session, player_id);
+ pdu->packet_type = AVRCP_PACKET_TYPE_SINGLE;
+
+ if (player) {
+ player->addressed = true;
+ status = AVRCP_STATUS_SUCCESS;
+ pdu->params_len = htons(len);
+ pdu->params[0] = status;
+ } else {
+ status = AVRCP_STATUS_INVALID_PLAYER_ID;
+ goto err;
+ }
+
+ /* Don't emit player changed immediately since PTS expect the
+ * response of SetAddressedPlayer before the event.
+ */
+ player->changed_id = g_idle_add(notify_addressed_player_changed,
+ player);
+
+ return AVC_CTYPE_ACCEPTED;
+
+err:
+ pdu->params_len = htons(sizeof(status));
+ pdu->params[0] = status;
+ return AVC_CTYPE_REJECTED;
+}
+
static const struct control_pdu_handler control_handlers[] = {
{ AVRCP_GET_CAPABILITIES, AVC_CTYPE_STATUS,
avrcp_handle_get_capabilities },
avrcp_handle_request_continuing },
{ AVRCP_ABORT_CONTINUING, AVC_CTYPE_CONTROL,
avrcp_handle_abort_continuing },
+ { AVRCP_SET_ADDRESSED_PLAYER, AVC_CTYPE_CONTROL,
+ avrcp_handle_set_addressed_player },
{ },
};
-#if defined(SUPPORT_AVRCP_TARGET) || defined(SUPPORT_AVRCP_CONTROL)
/* handle vendordep pdu inside an avctp packet */
static size_t handle_vendordep_pdu(struct avctp *conn, uint8_t transaction,
uint8_t *code, uint8_t *subunit,
return AVRCP_HEADER_LENGTH + 1;
}
+static void avrcp_handle_media_player_list(struct avrcp *session,
+ struct avrcp_browsing_header *pdu,
+ uint32_t start_item, uint32_t end_item)
+{
+ struct avrcp_player *player = session->target->player;
+ struct get_folder_items_rsp *rsp;
+ const char *name = NULL;
+ GSList *l;
+
+ rsp = (void *)pdu->params;
+ rsp->status = AVRCP_STATUS_SUCCESS;
+ rsp->uid_counter = htons(player_get_uid_counter(player));
+ rsp->num_items = 0;
+ pdu->param_len = sizeof(*rsp);
+
+ for (l = g_slist_nth(session->server->players, start_item);
+ l; l = g_slist_next(l)) {
+ struct avrcp_player *player = l->data;
+ struct folder_item *folder;
+ struct player_item *item;
+ uint16_t namelen;
+
+ if (rsp->num_items == (end_item - start_item) + 1)
+ break;
+
+ folder = (void *)&pdu->params[pdu->param_len];
+ folder->type = 0x01; /* Media Player */
+
+ pdu->param_len += sizeof(*folder);
+
+ item = (void *)folder->data;
+ item->player_id = htons(player->id);
+ item->type = 0x01; /* Audio */
+ item->subtype = htonl(0x01); /* Audio Book */
+ item->status = player_get_status(player);
+ /* Assign Default Feature Bit Mask */
+ memcpy(&item->features, &features, sizeof(features));
+
+ item->charset = htons(AVRCP_CHARSET_UTF8);
+
+ name = player->cb->get_name(player->user_data);
+ namelen = strlen(name);
+ item->namelen = htons(namelen);
+ memcpy(item->name, name, namelen);
+
+ folder->len = htons(sizeof(*item) + namelen);
+ pdu->param_len += sizeof(*item) + namelen;
+ rsp->num_items++;
+ }
+
+ /* If no player could be found respond with an error */
+ if (!rsp->num_items)
+ goto failed;
+
+ rsp->num_items = htons(rsp->num_items);
+ pdu->param_len = htons(pdu->param_len);
+
+ return;
+
+failed:
+ pdu->params[0] = AVRCP_STATUS_OUT_OF_BOUNDS;
+ pdu->param_len = htons(1);
+}
+
+static void avrcp_handle_get_folder_items(struct avrcp *session,
+ struct avrcp_browsing_header *pdu,
+ uint8_t transaction)
+{
+ uint32_t start_item = 0;
+ uint32_t end_item = 0;
+ uint8_t scope;
+ uint8_t status = AVRCP_STATUS_SUCCESS;
+
+ if (ntohs(pdu->param_len) < 10) {
+ status = AVRCP_STATUS_INVALID_PARAM;
+ goto failed;
+ }
+
+ scope = pdu->params[0];
+ start_item = bt_get_be32(&pdu->params[1]);
+ end_item = bt_get_be32(&pdu->params[5]);
+
+ DBG("scope 0x%02x start_item 0x%08x end_item 0x%08x", scope,
+ start_item, end_item);
+
+ if (end_item < start_item) {
+ status = AVRCP_STATUS_INVALID_PARAM;
+ goto failed;
+ }
+
+ switch (scope) {
+ case AVRCP_SCOPE_MEDIA_PLAYER_LIST:
+ avrcp_handle_media_player_list(session, pdu,
+ start_item, end_item);
+ break;
+ case AVRCP_SCOPE_MEDIA_PLAYER_VFS:
+ case AVRCP_SCOPE_SEARCH:
+ case AVRCP_SCOPE_NOW_PLAYING:
+ default:
+ status = AVRCP_STATUS_INVALID_PARAM;
+ goto failed;
+ }
+
+ return;
+
+failed:
+ pdu->params[0] = status;
+ pdu->param_len = htons(1);
+}
+
static struct browsing_pdu_handler {
uint8_t pdu_id;
void (*func) (struct avrcp *session, struct avrcp_browsing_header *pdu,
uint8_t transaction);
} browsing_handlers[] = {
+ { AVRCP_GET_FOLDER_ITEMS, avrcp_handle_get_folder_items },
{ },
};
-#endif
size_t avrcp_browsing_general_reject(uint8_t *operands)
{
return AVRCP_BROWSING_HEADER_LENGTH + sizeof(status);
}
-#if defined(SUPPORT_AVRCP_TARGET) || defined(SUPPORT_AVRCP_CONTROL)
static size_t handle_browsing_pdu(struct avctp *conn,
uint8_t transaction, uint8_t *operands,
size_t operand_count, void *user_data)
handler->func(session, pdu, transaction);
return AVRCP_BROWSING_HEADER_LENGTH + ntohs(pdu->param_len);
}
-#endif
size_t avrcp_handle_vendor_reject(uint8_t *code, uint8_t *operands)
{
return FALSE;
}
-#if defined(SUPPORT_AVRCP_TARGET) || defined(SUPPORT_AVRCP_CONTROL)
static void avrcp_get_current_player_value(struct avrcp *session,
uint8_t *attrs, uint8_t count)
{
avrcp_list_player_attributes_rsp,
session);
}
-#endif
static void avrcp_parse_attribute_list(struct avrcp_player *player,
uint8_t *operands, uint8_t count)
return 0;
}
+#ifdef __TIZEN_PATCH__
+static int ct_release(struct avrcp_player *player, uint8_t op)
+{
+ DBG("+");
+ int err;
+ struct avrcp *session;
+
+ session = player->sessions->data;
+ if (session == NULL)
+ return -ENOTCONN;
+ err = avctp_send_release_passthrough(session->conn, op);
+ if (err < 0)
+ return err;
+
+ DBG("-");
+ return 0;
+}
+#endif
static int ct_play(struct media_player *mp, void *user_data)
{
struct avrcp_player *player = user_data;
return ct_press(player, AVC_BACKWARD);
}
+#ifdef __TIZEN_PATCH__
+static int ct_press_fast_forward(struct media_player *mp, void *user_data)
+{
+ DBG("+");
+ struct avrcp_player *player = user_data;
+
+ DBG("-");
+ return ct_press(player, AVC_FAST_FORWARD);
+}
+
+static int ct_release_fast_forward(struct media_player *mp, void *user_data)
+{
+ DBG("+");
+ struct avrcp_player *player = user_data;
+
+ DBG("-");
+ return ct_release(player, AVC_FAST_FORWARD);
+}
+
+static int ct_press_rewind(struct media_player *mp, void *user_data)
+{
+ DBG("+");
+ struct avrcp_player *player = user_data;
+
+ DBG("-");
+ return ct_press(player, AVC_REWIND);
+}
+static int ct_release_rewind(struct media_player *mp, void *user_data)
+{
+ DBG("+");
+ struct avrcp_player *player = user_data;
+
+ DBG("-");
+ return ct_release(player, AVC_REWIND);
+}
+#else
static int ct_fast_forward(struct media_player *mp, void *user_data)
{
struct avrcp_player *player = user_data;
return ct_press(player, AVC_REWIND);
}
-
+#endif
static int ct_list_items(struct media_player *mp, const char *name,
uint32_t start, uint32_t end, void *user_data)
{
length = AVRCP_HEADER_LENGTH + ntohs(pdu->params_len);
- avctp_send_vendordep_req(session->conn, AVC_CTYPE_STATUS,
+ avctp_send_vendordep_req(session->conn, AVC_CTYPE_CONTROL,
AVC_SUBUNIT_PANEL, buf, length,
NULL, session);
}
length = AVRCP_HEADER_LENGTH + ntohs(pdu->params_len);
- avctp_send_vendordep_req(session->conn, AVC_CTYPE_STATUS,
+ avctp_send_vendordep_req(session->conn, AVC_CTYPE_CONTROL,
AVC_SUBUNIT_PANEL, buf, length,
NULL, session);
}
return 0;
}
+static gboolean avrcp_get_total_numberofitems_rsp(struct avctp *conn,
+ uint8_t *operands, size_t operand_count,
+ void *user_data)
+{
+ struct avrcp_browsing_header *pdu = (void *) operands;
+ struct avrcp *session = user_data;
+ struct avrcp_player *player = session->controller->player;
+ struct media_player *mp = player->user_data;
+ uint32_t num_of_items;
+
+ if (pdu == NULL)
+ return -ETIMEDOUT;
+
+ if (pdu->params[0] != AVRCP_STATUS_SUCCESS || operand_count < 7)
+ return -EINVAL;
+
+ if (pdu->params[0] == AVRCP_STATUS_OUT_OF_BOUNDS)
+ goto done;
+
+ player->uid_counter = get_be16(&pdu->params[1]);
+ num_of_items = get_be32(&pdu->params[3]);
+
+ if (!num_of_items)
+ return -EINVAL;
+
+done:
+ media_player_total_items_complete(mp, num_of_items);
+ return FALSE;
+}
+
+static void avrcp_get_total_numberofitems(struct avrcp *session)
+{
+ uint8_t buf[AVRCP_BROWSING_HEADER_LENGTH + 7];
+ struct avrcp_player *player = session->controller->player;
+ struct avrcp_browsing_header *pdu = (void *) buf;
+
+ memset(buf, 0, sizeof(buf));
+
+ pdu->pdu_id = AVRCP_GET_TOTAL_NUMBER_OF_ITEMS;
+ pdu->param_len = htons(7 + sizeof(uint32_t));
+
+ pdu->params[0] = player->scope;
+
+ avctp_send_browsing_req(session->conn, buf, sizeof(buf),
+ avrcp_get_total_numberofitems_rsp, session);
+}
+
+static int ct_get_total_numberofitems(struct media_player *mp, const char *name,
+ void *user_data)
+{
+ struct avrcp_player *player = user_data;
+ struct avrcp *session;
+
+ session = player->sessions->data;
+
+ if (session->controller->version != 0x0106) {
+ error("version not supported");
+ return -1;
+ }
+
+ if (g_str_has_prefix(name, "/NowPlaying"))
+ player->scope = 0x03;
+ else if (g_str_has_suffix(name, "/search"))
+ player->scope = 0x02;
+ else
+ player->scope = 0x01;
+
+ avrcp_get_total_numberofitems(session);
+
+ return 0;
+}
+
static const struct media_player_callback ct_cbs = {
.set_setting = ct_set_setting,
.play = ct_play,
.stop = ct_stop,
.next = ct_next,
.previous = ct_previous,
+#ifdef __TIZEN_PATCH__
+ .press_fast_forward = ct_press_fast_forward,
+ .release_fast_forward = ct_release_fast_forward,
+ .press_rewind = ct_press_rewind,
+ .release_rewind = ct_release_rewind,
+#else
.fast_forward = ct_fast_forward,
.rewind = ct_rewind,
+#endif
.list_items = ct_list_items,
.change_folder = ct_change_folder,
.search = ct_search,
.play_item = ct_play_item,
.add_to_nowplaying = ct_add_to_nowplaying,
+ .total_items = ct_get_total_numberofitems,
};
+static void set_ct_player(struct avrcp *session, struct avrcp_player *player)
+{
+ struct btd_service *service;
+
+ session->controller->player = player;
+ service = btd_device_get_service(session->dev, AVRCP_TARGET_UUID);
+ control_set_player(service, media_player_get_path(player->user_data));
+}
+
static struct avrcp_player *create_ct_player(struct avrcp *session,
uint16_t id)
{
player->destroy = (GDestroyNotify) media_player_destroy;
if (session->controller->player == NULL)
- session->controller->player = player;
+ set_ct_player(session, player);
session->controller->players = g_slist_prepend(
session->controller->players,
avrcp_stop_position_timer();
#endif
+ if (player->changed_id > 0)
+ g_source_remove(player->changed_id);
+
g_slist_free(player->sessions);
g_free(player->path);
g_free(player->change_path);
for (l = player->sessions; l; l = l->next) {
struct avrcp *session = l->data;
+ struct avrcp_data *controller = session->controller;
- session->controller->players = g_slist_remove(
- session->controller->players,
- player);
+ controller->players = g_slist_remove(controller->players,
+ player);
+
+ /* Check if current player is being removed */
+ if (controller->player == player)
+ set_ct_player(session, g_slist_nth_data(
+ controller->players, 0));
}
player_destroy(player);
i += len;
}
- if (g_slist_find(removed, session->controller->player))
- session->controller->player = NULL;
-
g_slist_free_full(removed, player_remove);
return FALSE;
memset(buf, 0, sizeof(buf));
pdu->pdu_id = AVRCP_GET_FOLDER_ITEMS;
+ put_be32(0, &pdu->params[1]);
+ put_be32(UINT32_MAX, &pdu->params[5]);
pdu->param_len = htons(10);
avctp_send_browsing_req(session->conn, buf, sizeof(buf),
avrcp_get_element_attributes(session);
}
+static void avrcp_playback_pos_changed(struct avrcp *session,
+ struct avrcp_header *pdu)
+{
+ struct avrcp_player *player = session->controller->player;
+ struct media_player *mp = player->user_data;
+ uint32_t position;
+
+ position = get_be32(&pdu->params[1]);
+ media_player_set_position(mp, position);
+}
+
static void avrcp_setting_changed(struct avrcp *session,
struct avrcp_header *pdu)
{
}
player->uid_counter = get_be16(&pdu->params[3]);
- session->controller->player = player;
+ set_ct_player(session, player);
if (player->features != NULL)
return;
case AVRCP_EVENT_TRACK_CHANGED:
avrcp_track_changed(session, pdu);
break;
+ case AVRCP_EVENT_PLAYBACK_POS_CHANGED:
+ avrcp_playback_pos_changed(session, pdu);
+ break;
case AVRCP_EVENT_SETTINGS_CHANGED:
avrcp_setting_changed(session, pdu);
break;
pdu->pdu_id = AVRCP_REGISTER_NOTIFICATION;
pdu->packet_type = AVRCP_PACKET_TYPE_SINGLE;
pdu->params[0] = event;
+
+ /*
+ * Set maximum interval possible for position changed as we only
+ * use it to resync.
+ */
+ if (event == AVRCP_EVENT_PLAYBACK_POS_CHANGED)
+ bt_put_be32(UINT32_MAX / 1000, &pdu->params[1]);
+
pdu->params_len = htons(AVRCP_REGISTER_NOTIFICATION_PARAM_LENGTH);
length = AVRCP_HEADER_LENGTH + ntohs(pdu->params_len);
avrcp_handle_event, session);
}
-#if defined(SUPPORT_AVRCP_TARGET) || defined(SUPPORT_AVRCP_CONTROL)
+#ifdef __TIZEN_PATCH__
+static char *avrcp_event_to_string(uint8_t event)
+{
+
+ switch (event) {
+ case AVRCP_EVENT_STATUS_CHANGED:
+ return "AVRCP EVENT STATUS CHANGED";
+ case AVRCP_EVENT_TRACK_CHANGED:
+ return "AVRCP EVENT TRACK CHANGED";
+ case AVRCP_EVENT_SETTINGS_CHANGED:
+ return "AVRCP EVENT SETTINGS CHANGED";
+ case AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED:
+ return "AVRCP EVENT ADDRESSED PLAYER CHANGED";
+ case AVRCP_EVENT_UIDS_CHANGED:
+ return "AVRCP EVENT UIDS CHANGED";
+ case AVRCP_EVENT_AVAILABLE_PLAYERS_CHANGED:
+ return "AVRCP EVENT AVAILABLE PLAYERS CHANGED";
+ case AVRCP_EVENT_VOLUME_CHANGED:
+ return "AVRCP EVENT VOLUME CHANGED";
+ default:
+ return "Unknown Event";
+ }
+}
+
+static gboolean avrcp_get_playback_status(struct avrcp *session)
+{
+ avrcp_get_play_status(session);
+ return TRUE;
+}
+#endif
+
static gboolean avrcp_get_capabilities_resp(struct avctp *conn,
uint8_t code, uint8_t subunit,
uint8_t *operands, size_t operand_count,
uint8_t event = pdu->params[1 + count];
events |= (1 << event);
-
+#ifdef __TIZEN_PATCH__
+ DBG("Supported Event %s", avrcp_event_to_string(event));
+#endif
switch (event) {
case AVRCP_EVENT_STATUS_CHANGED:
case AVRCP_EVENT_TRACK_CHANGED:
+ case AVRCP_EVENT_PLAYBACK_POS_CHANGED:
case AVRCP_EVENT_SETTINGS_CHANGED:
#ifndef __TIZEN_PATCH__
case AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED:
if (!(events & (1 << AVRCP_EVENT_STATUS_CHANGED)))
avrcp_get_element_attributes(session);
-
+#ifdef __TIZEN_PATCH__
+ if ((events & (1 << AVRCP_EVENT_STATUS_CHANGED)) == 0) {
+ session->playback_status_id = g_timeout_add_seconds(1,
+ avrcp_get_playback_status, session);
+ }
+#endif
return FALSE;
}
avrcp_get_capabilities_resp,
session);
}
-#endif
static struct avrcp *find_session(GSList *list, struct btd_device *dev)
{
return NULL;
}
-#if defined(SUPPORT_AVRCP_TARGET) || defined(SUPPORT_AVRCP_CONTROL)
static void destroy_browsing(void *data)
{
struct avrcp *session = data;
connect_browsing,
session);
}
-#endif
#ifdef SUPPORT_AVRCP_TARGET
static void target_init(struct avrcp *session)
return;
#endif
+ session->supported_events |=
+ (1 << AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED) |
+ (1 << AVRCP_EVENT_AVAILABLE_PLAYERS_CHANGED) |
+ (1 << AVRCP_EVENT_VOLUME_CHANGED);
+
/* Only check capabilities if controller is not supported */
if (session->controller == NULL)
avrcp_get_capabilities(session);
return;
controller = data_init(session, AVRCP_TARGET_UUID);
-#ifdef __TIZEN_PATCH__
- /* Fix : NULL_RETURNS */
- if (controller == NULL) {
- error("controller is NULL");
- return;
- }
-#endif
session->controller = controller;
DBG("%p version 0x%04x", controller, controller->version);
#ifdef __TIZEN_PATCH__
if ((controller->version >= 0x0104) && (adapter_avrcp_ct_ver >= 0x0104))
session->supported_events |= (1 << AVRCP_EVENT_VOLUME_CHANGED);
-#else
- if (controller->version >= 0x0104)
- session->supported_events |= (1 << AVRCP_EVENT_VOLUME_CHANGED);
#endif
service = btd_device_get_service(session->dev, AVRCP_TARGET_UUID);
}
#endif
-#if defined(SUPPORT_AVRCP_TARGET) || defined(SUPPORT_AVRCP_CONTROL)
static void session_init_control(struct avrcp *session)
{
session->passthrough_id = avctp_register_passthrough_handler(
server->sessions = g_slist_remove(server->sessions, session);
+#ifdef __TIZEN_PATCH__
+ if (session->playback_status_id > 0) {
+ DBG("Removing the timer for playback status polling");
+ g_source_remove(session->playback_status_id);
+ session->playback_status_id = 0;
+ }
+#endif
+
session_abort_pending_pdu(session);
service = btd_device_get_service(session->dev, AVRCP_TARGET_UUID);
avctp_id = 0;
}
}
-#endif
struct avrcp_player *avrcp_register_player(struct btd_adapter *adapter,
struct avrcp_player_cb *cb,
struct avrcp_server *server;
struct avrcp_player *player;
GSList *l;
+ static uint16_t id = 0;
server = find_server(servers, adapter);
if (!server)
return NULL;
player = g_new0(struct avrcp_player, 1);
+ player->id = ++id;
player->server = server;
player->cb = cb;
player->user_data = user_data;
}
}
+ avrcp_player_event(player,
+ AVRCP_EVENT_AVAILABLE_PLAYERS_CHANGED, NULL);
+
return player;
}
target->player = g_slist_nth_data(server->players, 0);
}
+ avrcp_player_event(player,
+ AVRCP_EVENT_AVAILABLE_PLAYERS_CHANGED, NULL);
+
player_destroy(player);
}
return FALSE;
}
-int avrcp_set_volume(struct btd_device *dev, uint8_t volume)
+static int avrcp_event(struct avrcp *session, uint8_t id, const void *data)
+{
+ uint8_t buf[AVRCP_HEADER_LENGTH + 2];
+ struct avrcp_header *pdu = (void *) buf;
+ uint8_t code;
+ uint16_t size;
+ int err;
+
+ /* Verify that the event is registered */
+ if (!(session->registered_events & (1 << id)))
+ return -ENOENT;
+
+ memset(buf, 0, sizeof(buf));
+
+ set_company_id(pdu->company_id, IEEEID_BTSIG);
+ pdu->pdu_id = AVRCP_REGISTER_NOTIFICATION;
+ code = AVC_CTYPE_CHANGED;
+ pdu->params[0] = id;
+
+ DBG("id=%u", id);
+
+ switch (id) {
+ case AVRCP_EVENT_VOLUME_CHANGED:
+ size = 2;
+ memcpy(&pdu->params[1], data, sizeof(uint8_t));
+ break;
+ default:
+ error("Unknown event %u", id);
+ return -EINVAL;
+ }
+
+ pdu->params_len = htons(size);
+
+ err = avctp_send_vendordep(session->conn,
+ session->transaction_events[id],
+ code, AVC_SUBUNIT_PANEL,
+ buf, size + AVRCP_HEADER_LENGTH);
+ if (err < 0)
+ return err;
+
+ /* Unregister event as per AVRCP 1.3 spec, section 5.4.2 */
+ session->registered_events ^= 1 << id;
+
+ return err;
+}
+
+int avrcp_set_volume(struct btd_device *dev, uint8_t volume, bool notify)
{
struct avrcp_server *server;
struct avrcp *session;
return -EINVAL;
session = find_session(server->sessions, dev);
- if (session == NULL || session->target == NULL)
+ if (session == NULL)
return -ENOTCONN;
- if (session->target->version < 0x0104)
+ if (notify) {
+ if (!session->target)
+ return -ENOTSUP;
+ return avrcp_event(session, AVRCP_EVENT_VOLUME_CHANGED,
+ &volume);
+ }
+
+ if (!session->controller || session->controller->version < 0x0104)
return -ENOTSUP;
memset(buf, 0, sizeof(buf));
set_company_id(pdu->company_id, IEEEID_BTSIG);
- DBG("volume=%u", volume);
-
pdu->pdu_id = AVRCP_SET_ABSOLUTE_VOLUME;
pdu->params[0] = volume;
pdu->params_len = htons(1);
{
struct btd_device *dev = btd_service_get_device(service);
const char *path = device_get_path(dev);
-
+#ifdef __TIZEN_PATCH__
+ char name[10];
+#endif
DBG("path %s", path);
+#ifdef __TIZEN_PATCH__
+ device_get_name(dev, name, sizeof(name));
+ DBG("name : %s", name);
+ if (g_str_equal(name, "PLT_M50")) {
+ DBG("Don't initiate avrcp connection with this headset");
+ return -ENOTSUP;
+ }
+#endif
return control_connect(service);
}