* [PATCH BlueZ 02/16] AVCTP: Allocate memory to hold incoming/outgoing PDUs
2012-10-15 14:05 [PATCH BlueZ 01/16] AVCTP: Simplify channel handling Luiz Augusto von Dentz
@ 2012-10-15 14:05 ` Luiz Augusto von Dentz
2012-10-15 14:05 ` [PATCH BlueZ 03/16] AVRCP: Register to AVCTP state changes without depending on player Luiz Augusto von Dentz
` (13 subsequent siblings)
14 siblings, 0 replies; 17+ messages in thread
From: Luiz Augusto von Dentz @ 2012-10-15 14:05 UTC (permalink / raw)
To: linux-bluetooth
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
This makes possible to the handler to respond asyncronous as the memory
remains valid after it returns.
In addition to that it uses the MTU to calculate the buffer size
necessary.
---
audio/avctp.c | 31 ++++++++++++++++++++++---------
1 file changed, 22 insertions(+), 9 deletions(-)
diff --git a/audio/avctp.c b/audio/avctp.c
index 092a5e2..aa9a1ca 100644
--- a/audio/avctp.c
+++ b/audio/avctp.c
@@ -134,7 +134,9 @@ struct avctp_rsp_handler {
struct avctp_channel {
GIOChannel *io;
guint watch;
- uint16_t mtu;
+ uint16_t imtu;
+ uint16_t omtu;
+ uint8_t *buffer;
};
struct avctp {
@@ -343,6 +345,7 @@ static void avctp_channel_destroy(struct avctp_channel *chan)
if (chan->watch)
g_source_remove(chan->watch);
+ g_free(chan->buffer);
g_free(chan);
}
@@ -446,7 +449,9 @@ static gboolean session_browsing_cb(GIOChannel *chan, GIOCondition cond,
gpointer data)
{
struct avctp *session = data;
- uint8_t buf[1024], *operands;
+ struct avctp_channel *browsing = session->browsing;
+ uint8_t *buf = browsing->buffer;
+ uint8_t *operands;
struct avctp_header *avctp;
int sock, ret, packet_size, operand_count;
@@ -455,7 +460,7 @@ static gboolean session_browsing_cb(GIOChannel *chan, GIOCondition cond,
sock = g_io_channel_unix_get_fd(chan);
- ret = read(sock, buf, sizeof(buf));
+ ret = read(sock, buf, sizeof(browsing->imtu));
if (ret <= 0)
goto failed;
@@ -496,7 +501,9 @@ static gboolean session_cb(GIOChannel *chan, GIOCondition cond,
gpointer data)
{
struct avctp *session = data;
- uint8_t buf[1024], *operands, code, subunit;
+ struct avctp_channel *control = session->control;
+ uint8_t *buf = control->buffer;
+ uint8_t *operands, code, subunit;
struct avctp_header *avctp;
struct avc_header *avc;
int ret, packet_size, operand_count, sock;
@@ -507,7 +514,7 @@ static gboolean session_cb(GIOChannel *chan, GIOCondition cond,
sock = g_io_channel_unix_get_fd(chan);
- ret = read(sock, buf, sizeof(buf));
+ ret = read(sock, buf, control->imtu);
if (ret <= 0)
goto failed;
@@ -688,7 +695,7 @@ static void avctp_connect_browsing_cb(GIOChannel *chan, GError *err,
{
struct avctp *session = data;
char address[18];
- uint16_t imtu;
+ uint16_t imtu, omtu;
GError *gerr = NULL;
if (err) {
@@ -699,6 +706,7 @@ static void avctp_connect_browsing_cb(GIOChannel *chan, GError *err,
bt_io_get(chan, &gerr,
BT_IO_OPT_DEST, &address,
BT_IO_OPT_IMTU, &imtu,
+ BT_IO_OPT_OMTU, &omtu,
BT_IO_OPT_INVALID);
if (gerr) {
error("%s", gerr->message);
@@ -713,7 +721,9 @@ static void avctp_connect_browsing_cb(GIOChannel *chan, GError *err,
if (session->browsing == NULL)
session->browsing = avctp_channel_create(chan);
- session->browsing->mtu = imtu;
+ session->browsing->imtu = imtu;
+ session->browsing->omtu = omtu;
+ session->browsing->buffer = g_malloc0(MAX(imtu, omtu));
session->browsing->watch = g_io_add_watch(session->browsing->io,
G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
(GIOFunc) session_browsing_cb, session);
@@ -731,7 +741,7 @@ static void avctp_connect_cb(GIOChannel *chan, GError *err, gpointer data)
{
struct avctp *session = data;
char address[18];
- uint16_t imtu;
+ uint16_t imtu, omtu;
GError *gerr = NULL;
if (err) {
@@ -743,6 +753,7 @@ static void avctp_connect_cb(GIOChannel *chan, GError *err, gpointer data)
bt_io_get(chan, &gerr,
BT_IO_OPT_DEST, &address,
BT_IO_OPT_IMTU, &imtu,
+ BT_IO_OPT_IMTU, &omtu,
BT_IO_OPT_INVALID);
if (gerr) {
avctp_set_state(session, AVCTP_STATE_DISCONNECTED);
@@ -756,7 +767,9 @@ static void avctp_connect_cb(GIOChannel *chan, GError *err, gpointer data)
if (session->control == NULL)
session->control = avctp_channel_create(chan);
- session->control->mtu = imtu;
+ session->control->imtu = imtu;
+ session->control->omtu = omtu;
+ session->control->buffer = g_malloc0(MAX(imtu, omtu));
session->control->watch = g_io_add_watch(session->control->io,
G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
(GIOFunc) session_cb, session);
--
1.7.11.4
^ permalink raw reply related [flat|nested] 17+ messages in thread* [PATCH BlueZ 03/16] AVRCP: Register to AVCTP state changes without depending on player
2012-10-15 14:05 [PATCH BlueZ 01/16] AVCTP: Simplify channel handling Luiz Augusto von Dentz
2012-10-15 14:05 ` [PATCH BlueZ 02/16] AVCTP: Allocate memory to hold incoming/outgoing PDUs Luiz Augusto von Dentz
@ 2012-10-15 14:05 ` Luiz Augusto von Dentz
2012-10-15 14:05 ` [PATCH BlueZ 04/16] AVRCP: Don't respond with errors when no player is registered Luiz Augusto von Dentz
` (12 subsequent siblings)
14 siblings, 0 replies; 17+ messages in thread
From: Luiz Augusto von Dentz @ 2012-10-15 14:05 UTC (permalink / raw)
To: linux-bluetooth
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
It is not longer necessary to have a player to be able to register the
extra pdu handlers.
---
audio/avrcp.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/audio/avrcp.c b/audio/avrcp.c
index aa62987..295a1e1 100644
--- a/audio/avrcp.c
+++ b/audio/avrcp.c
@@ -1467,6 +1467,9 @@ int avrcp_register(const bdaddr_t *src, GKeyFile *config)
servers = g_slist_append(servers, server);
+ if (!avctp_id)
+ avctp_id = avctp_add_state_cb(state_changed, NULL);
+
return 0;
}
@@ -1528,9 +1531,6 @@ struct avrcp_player *avrcp_register_player(const bdaddr_t *src,
player->user_data = user_data;
player->destroy = destroy;
- if (!avctp_id)
- avctp_id = avctp_add_state_cb(state_changed, NULL);
-
server->players = g_slist_append(server->players, player);
/* Assign player to session without current player */
--
1.7.11.4
^ permalink raw reply related [flat|nested] 17+ messages in thread* [PATCH BlueZ 04/16] AVRCP: Don't respond with errors when no player is registered
2012-10-15 14:05 [PATCH BlueZ 01/16] AVCTP: Simplify channel handling Luiz Augusto von Dentz
2012-10-15 14:05 ` [PATCH BlueZ 02/16] AVCTP: Allocate memory to hold incoming/outgoing PDUs Luiz Augusto von Dentz
2012-10-15 14:05 ` [PATCH BlueZ 03/16] AVRCP: Register to AVCTP state changes without depending on player Luiz Augusto von Dentz
@ 2012-10-15 14:05 ` Luiz Augusto von Dentz
2012-10-15 14:37 ` Johan Hedberg
2012-10-15 14:05 ` [PATCH BlueZ 05/16] AVRCP: Fix crash on disconnect Luiz Augusto von Dentz
` (11 subsequent siblings)
14 siblings, 1 reply; 17+ messages in thread
From: Luiz Augusto von Dentz @ 2012-10-15 14:05 UTC (permalink / raw)
To: linux-bluetooth
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
Some devices w.g. Sony MW600 will stop using certain commands if an
error happen, so the code now just fake a player and once a real
player is registered it takes place of the fake one.
---
audio/avrcp.c | 77 ++++++++++++++++++++++++++++++++++++++++++++++++-----------
1 file changed, 63 insertions(+), 14 deletions(-)
diff --git a/audio/avrcp.c b/audio/avrcp.c
index 295a1e1..1ed6a24 100644
--- a/audio/avrcp.c
+++ b/audio/avrcp.c
@@ -477,6 +477,17 @@ void avrcp_player_event(struct avrcp_player *player, uint8_t id, void *data)
return;
}
+static void *player_get_metadata(struct avrcp_player *player, uint32_t attr)
+{
+ if (player != NULL)
+ return player->cb->get_metadata(attr, player->user_data);
+
+ if (attr == AVRCP_MEDIA_ATTRIBUTE_TITLE)
+ return "";
+
+ return NULL;
+}
+
static uint16_t player_write_media_attribute(struct avrcp_player *player,
uint32_t id, uint8_t *buf,
uint16_t *pos,
@@ -489,7 +500,7 @@ static uint16_t player_write_media_attribute(struct avrcp_player *player,
DBG("%u", id);
- value = player->cb->get_metadata(id, player->user_data);
+ value = player_get_metadata(player, id);
if (value == NULL) {
*offset = 0;
return 0;
@@ -588,6 +599,9 @@ static int player_set_attribute(struct avrcp_player *player,
{
DBG("Change attribute: %u %u", attr, val);
+ if (player == NULL)
+ return -ENOENT;
+
return player->cb->set_setting(attr, val, player->user_data);
}
@@ -597,6 +611,9 @@ static int player_get_attribute(struct avrcp_player *player, uint8_t attr)
DBG("attr %u", attr);
+ if (player == NULL)
+ return -ENOENT;
+
value = player->cb->get_setting(attr, player->user_data);
if (value < 0)
DBG("attr %u not supported by player", attr);
@@ -653,7 +670,7 @@ static uint8_t avrcp_handle_list_player_attributes(struct avrcp *session,
uint16_t len = ntohs(pdu->params_len);
unsigned int i;
- if (len != 0 || player == NULL) {
+ if (len != 0) {
pdu->params_len = htons(1);
pdu->params[0] = AVRCP_STATUS_INVALID_PARAM;
return AVC_CTYPE_REJECTED;
@@ -685,7 +702,7 @@ static uint8_t avrcp_handle_list_player_values(struct avrcp *session,
uint16_t len = ntohs(pdu->params_len);
unsigned int i;
- if (len != 1 || player == NULL)
+ if (len != 1)
goto err;
if (player_get_attribute(player, pdu->params[0]) < 0)
@@ -707,6 +724,15 @@ err:
return AVC_CTYPE_REJECTED;
}
+static GList *player_list_metadata(struct avrcp_player *player)
+{
+ if (player != NULL)
+ return player->cb->list_metadata(player->user_data);
+
+ return g_list_prepend(NULL,
+ GUINT_TO_POINTER(AVRCP_MEDIA_ATTRIBUTE_TITLE));
+}
+
static uint8_t avrcp_handle_get_element_attributes(struct avrcp *session,
struct avrcp_header *pdu,
uint8_t transaction)
@@ -719,7 +745,7 @@ static uint8_t avrcp_handle_get_element_attributes(struct avrcp *session,
GList *attr_ids;
uint16_t offset;
- if (len < 9 || identifier != 0 || player == NULL)
+ if (len < 9 || identifier != 0)
goto err;
nattr = pdu->params[8];
@@ -732,7 +758,7 @@ static uint8_t avrcp_handle_get_element_attributes(struct avrcp *session,
* Return all available information, at least
* title must be returned if there's a track selected.
*/
- attr_ids = player->cb->list_metadata(player->user_data);
+ attr_ids = player_list_metadata(player);
len = g_list_length(attr_ids);
} else {
unsigned int i;
@@ -788,8 +814,7 @@ static uint8_t avrcp_handle_get_current_player_value(struct avrcp *session,
uint8_t *settings;
unsigned int i;
- if (player == NULL || len <= 1 || pdu->params[0] != len - 1 ||
- player == NULL)
+ if (len <= 1 || pdu->params[0] != len - 1)
goto err;
/*
@@ -922,6 +947,14 @@ err:
return AVC_CTYPE_REJECTED;
}
+static uint32_t player_get_position(struct avrcp_player *player)
+{
+ if (player == NULL)
+ return 0;
+
+ return player->cb->get_position(player->user_data);
+}
+
static uint8_t avrcp_handle_get_play_status(struct avrcp *session,
struct avrcp_header *pdu,
uint8_t transaction)
@@ -932,15 +965,15 @@ static uint8_t avrcp_handle_get_play_status(struct avrcp *session,
uint32_t duration;
void *pduration;
- if (len != 0 || player == NULL) {
+ if (len != 0) {
pdu->params_len = htons(1);
pdu->params[0] = AVRCP_STATUS_INVALID_PARAM;
return AVC_CTYPE_REJECTED;
}
- position = player->cb->get_position(player->user_data);
- pduration = player->cb->get_metadata(AVRCP_MEDIA_ATTRIBUTE_DURATION,
- player->user_data);
+ position = player_get_position(player);
+ pduration = player_get_metadata(player,
+ AVRCP_MEDIA_ATTRIBUTE_DURATION);
if (pduration != NULL)
duration = htonl(GPOINTER_TO_UINT(pduration));
else
@@ -957,6 +990,22 @@ static uint8_t avrcp_handle_get_play_status(struct avrcp *session,
return AVC_CTYPE_STABLE;
}
+static uint64_t player_get_uid(struct avrcp_player *player)
+{
+ if (player == NULL)
+ return UINT64_MAX;
+
+ return player->cb->get_status(player->user_data);
+}
+
+static uint8_t player_get_status(struct avrcp_player *player)
+{
+ if (player == NULL)
+ return AVRCP_PLAY_STATUS_STOPPED;
+
+ return player->cb->get_status(player->user_data);
+}
+
static uint8_t avrcp_handle_register_notification(struct avrcp *session,
struct avrcp_header *pdu,
uint8_t transaction)
@@ -970,18 +1019,18 @@ static uint8_t avrcp_handle_register_notification(struct avrcp *session,
* one is applicable only for EVENT_PLAYBACK_POS_CHANGED. See AVRCP
* 1.3 spec, section 5.4.2.
*/
- if (len != 5 || player == NULL)
+ if (len != 5)
goto err;
switch (pdu->params[0]) {
case AVRCP_EVENT_STATUS_CHANGED:
len = 2;
- pdu->params[1] = player->cb->get_status(player->user_data);
+ pdu->params[1] = player_get_status(player);
break;
case AVRCP_EVENT_TRACK_CHANGED:
len = 9;
- uid = player->cb->get_uid(player->user_data);
+ uid = player_get_uid(player);
memcpy(&pdu->params[1], &uid, sizeof(uint64_t));
break;
--
1.7.11.4
^ permalink raw reply related [flat|nested] 17+ messages in thread* Re: [PATCH BlueZ 04/16] AVRCP: Don't respond with errors when no player is registered
2012-10-15 14:05 ` [PATCH BlueZ 04/16] AVRCP: Don't respond with errors when no player is registered Luiz Augusto von Dentz
@ 2012-10-15 14:37 ` Johan Hedberg
0 siblings, 0 replies; 17+ messages in thread
From: Johan Hedberg @ 2012-10-15 14:37 UTC (permalink / raw)
To: Luiz Augusto von Dentz; +Cc: linux-bluetooth
Hi Luiz,
On Mon, Oct 15, 2012, Luiz Augusto von Dentz wrote:
> +static void *player_get_metadata(struct avrcp_player *player, uint32_t attr)
> +{
> + if (player != NULL)
> + return player->cb->get_metadata(attr, player->user_data);
> +
> + if (attr == AVRCP_MEDIA_ATTRIBUTE_TITLE)
> + return "";
This is quite messed up. Using void pointers like that just obfuscates
what the code is doing. After a quick look at current git it seems to me
like struct metadata_value from media.c should be made public and that
should be the return type of this function instead of doing void pointer
magic.
Johan
^ permalink raw reply [flat|nested] 17+ messages in thread
* [PATCH BlueZ 05/16] AVRCP: Fix crash on disconnect
2012-10-15 14:05 [PATCH BlueZ 01/16] AVCTP: Simplify channel handling Luiz Augusto von Dentz
` (2 preceding siblings ...)
2012-10-15 14:05 ` [PATCH BlueZ 04/16] AVRCP: Don't respond with errors when no player is registered Luiz Augusto von Dentz
@ 2012-10-15 14:05 ` Luiz Augusto von Dentz
2012-10-15 14:05 ` [PATCH BlueZ 06/16] AVRCP: Simplify state_changed callback Luiz Augusto von Dentz
` (10 subsequent siblings)
14 siblings, 0 replies; 17+ messages in thread
From: Luiz Augusto von Dentz @ 2012-10-15 14:05 UTC (permalink / raw)
To: linux-bluetooth
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
In case of multiple session being active the code was registering one
PDU hanlder per AVRCP session and given the session pointer as user data
causing the following:
24 bytes in 1 blocks are definitely lost in loss record 370 of 893
at 0x4A0884D: malloc (vg_replace_malloc.c:263)
by 0x4C803FE: g_malloc (in /usr/lib64/libglib-2.0.so.0.3200.4)
by 0x12EE9D: avctp_register_browsing_pdu_handler (avctp.c:1259)
by 0x12FD7B: state_changed (avrcp.c:1402)
by 0x12D391: avctp_set_state (avctp.c:403)
by 0x12E6B4: avctp_confirm_cb (avctp.c:871)
by 0x1606A3: server_cb (btio.c:254)
by 0x4C7A824: g_main_context_dispatch (in /usr/lib64/libglib-2.0.so.0.3200.4)
by 0x4C7AB57: ??? (in /usr/lib64/libglib-2.0.so.0.3200.4)
by 0x4C7AF51: g_main_loop_run (in /usr/lib64/libglib-2.0.so.0.3200.4)
by 0x120EA1: main (main.c:551)
To fix this the PDU handlers are now per AVCTP session/channel so each
AVRCP session can register its own handler and pass itself as user
data.
---
audio/avctp.c | 180 ++++++++++++++++++++++++++++++++++++----------------------
audio/avctp.h | 10 ++--
audio/avrcp.c | 56 +++++++++---------
audio/avrcp.h | 1 +
4 files changed, 148 insertions(+), 99 deletions(-)
diff --git a/audio/avctp.c b/audio/avctp.c
index aa9a1ca..228d5b7 100644
--- a/audio/avctp.c
+++ b/audio/avctp.c
@@ -137,6 +137,7 @@ struct avctp_channel {
uint16_t imtu;
uint16_t omtu;
uint8_t *buffer;
+ GSList *handlers;
};
struct avctp {
@@ -148,6 +149,9 @@ struct avctp {
int uinput;
guint auth_id;
+ unsigned int passthrough_id;
+ unsigned int unit_id;
+ unsigned int subunit_id;
struct avctp_channel *control;
struct avctp_channel *browsing;
@@ -186,9 +190,7 @@ static struct {
static GSList *callbacks = NULL;
static GSList *servers = NULL;
-static GSList *control_handlers = NULL;
static uint8_t id = 0;
-static struct avctp_browsing_pdu_handler *browsing_handler = NULL;
static void auth_cb(DBusError *derr, void *user_data);
@@ -346,6 +348,7 @@ static void avctp_channel_destroy(struct avctp_channel *chan)
g_source_remove(chan->watch);
g_free(chan->buffer);
+ g_slist_free_full(chan->handlers, g_free);
g_free(chan);
}
@@ -454,6 +457,7 @@ static gboolean session_browsing_cb(GIOChannel *chan, GIOCondition cond,
uint8_t *operands;
struct avctp_header *avctp;
int sock, ret, packet_size, operand_count;
+ struct avctp_browsing_pdu_handler *handler;
if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL))
goto failed;
@@ -480,10 +484,18 @@ static gboolean session_browsing_cb(GIOChannel *chan, GIOCondition cond,
packet_size = AVCTP_HEADER_LENGTH;
avctp->cr = AVCTP_RESPONSE;
- packet_size += browsing_handler->cb(session, avctp->transaction,
+ handler = g_slist_nth_data(browsing->handlers, 0);
+ if (handler == NULL) {
+ DBG("handler not found");
+ packet_size += avrcp_browsing_general_reject(operands);
+ goto send;
+ }
+
+ packet_size += handler->cb(session, avctp->transaction,
operands, operand_count,
- browsing_handler->user_data);
+ handler->user_data);
+send:
if (packet_size != 0) {
ret = write(sock, buf, packet_size);
if (ret != packet_size)
@@ -570,7 +582,7 @@ static gboolean session_cb(GIOChannel *chan, GIOCondition cond,
goto done;
}
- handler = find_handler(control_handlers, avc->opcode);
+ handler = find_handler(control->handlers, avc->opcode);
if (!handler) {
DBG("handler not found for 0x%02x", avc->opcode);
packet_size += avrcp_handle_vendor_reject(&code, operands);
@@ -774,6 +786,19 @@ static void avctp_connect_cb(GIOChannel *chan, GError *err, gpointer data)
G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
(GIOFunc) session_cb, session);
+ session->passthrough_id = avctp_register_pdu_handler(session,
+ AVC_OP_PASSTHROUGH,
+ handle_panel_passthrough,
+ NULL);
+ session->unit_id = avctp_register_pdu_handler(session,
+ AVC_OP_UNITINFO,
+ handle_unit_info,
+ NULL);
+ session->subunit_id = avctp_register_pdu_handler(session,
+ AVC_OP_SUBUNITINFO,
+ handle_subunit_info,
+ NULL);
+
init_uinput(session);
avctp_set_state(session, AVCTP_STATE_CONNECTED);
@@ -896,12 +921,6 @@ static void avctp_browsing_confirm(struct avctp *session, GIOChannel *chan,
return;
}
- if (browsing_handler == NULL) {
- error("Browsing: Handler not registered");
- g_io_channel_shutdown(chan, TRUE, NULL);
- return;
- }
-
if (bt_io_accept(chan, avctp_connect_browsing_cb, session, NULL,
&err))
return;
@@ -994,10 +1013,6 @@ static GIOChannel *avctp_server_socket(const bdaddr_t *src, gboolean master,
return io;
}
-static unsigned int passthrough_id = 0;
-static unsigned int unit_id = 0;
-static unsigned int subunit_id = 0;
-
int avctp_register(const bdaddr_t *src, gboolean master)
{
struct avctp_server *server;
@@ -1026,18 +1041,6 @@ int avctp_register(const bdaddr_t *src, gboolean master)
servers = g_slist_append(servers, server);
- if (!passthrough_id)
- passthrough_id = avctp_register_pdu_handler(AVC_OP_PASSTHROUGH,
- handle_panel_passthrough, NULL);
-
- if (!unit_id)
- unit_id = avctp_register_pdu_handler(AVC_OP_UNITINFO, handle_unit_info,
- NULL);
-
- if (!subunit_id)
- subunit_id = avctp_register_pdu_handler(AVC_OP_SUBUNITINFO,
- handle_subunit_info, NULL);
-
return 0;
}
@@ -1061,24 +1064,6 @@ void avctp_unregister(const bdaddr_t *src)
g_io_channel_shutdown(server->control_io, TRUE, NULL);
g_io_channel_unref(server->control_io);
g_free(server);
-
- if (servers)
- return;
-
- if (passthrough_id) {
- avctp_unregister_pdu_handler(passthrough_id);
- passthrough_id = 0;
- }
-
- if (unit_id) {
- avctp_unregister_pdu_handler(unit_id);
- passthrough_id = 0;
- }
-
- if (subunit_id) {
- avctp_unregister_pdu_handler(subunit_id);
- subunit_id = 0;
- }
}
int avctp_send_passthrough(struct avctp *session, uint8_t op)
@@ -1230,13 +1215,18 @@ gboolean avctp_remove_state_cb(unsigned int id)
return FALSE;
}
-unsigned int avctp_register_pdu_handler(uint8_t opcode, avctp_control_pdu_cb cb,
- void *user_data)
+unsigned int avctp_register_pdu_handler(struct avctp *session, uint8_t opcode,
+ avctp_control_pdu_cb cb,
+ void *user_data)
{
+ struct avctp_channel *control = session->control;
struct avctp_pdu_handler *handler;
static unsigned int id = 0;
- handler = find_handler(control_handlers, opcode);
+ if (control == NULL)
+ return 0;
+
+ handler = find_handler(control->handlers, opcode);
if (handler)
return 0;
@@ -1246,36 +1236,63 @@ unsigned int avctp_register_pdu_handler(uint8_t opcode, avctp_control_pdu_cb cb,
handler->user_data = user_data;
handler->id = ++id;
- control_handlers = g_slist_append(control_handlers, handler);
+ control->handlers = g_slist_append(control->handlers, handler);
return handler->id;
}
-unsigned int avctp_register_browsing_pdu_handler(avctp_browsing_pdu_cb cb,
- void *user_data)
+unsigned int avctp_register_browsing_pdu_handler(struct avctp *session,
+ avctp_browsing_pdu_cb cb,
+ void *user_data)
{
- unsigned int id = 0;
+ struct avctp_channel *browsing = session->browsing;
+ struct avctp_browsing_pdu_handler *handler;
+ static unsigned int id = 0;
+
+ if (browsing == NULL)
+ return 0;
+
+ if (browsing->handlers != NULL)
+ return 0;
+
+ handler = g_new(struct avctp_browsing_pdu_handler, 1);
+ handler->cb = cb;
+ handler->user_data = user_data;
+ handler->id = ++id;
- browsing_handler = g_new(struct avctp_browsing_pdu_handler, 1);
- browsing_handler->cb = cb;
- browsing_handler->user_data = user_data;
- browsing_handler->id = ++id;
+ browsing->handlers = g_slist_append(browsing->handlers, handler);
- return browsing_handler->id;
+ return handler->id;
}
gboolean avctp_unregister_pdu_handler(unsigned int id)
{
GSList *l;
- for (l = control_handlers; l != NULL; l = l->next) {
- struct avctp_pdu_handler *handler = l->data;
+ for (l = servers; l; l = l->next) {
+ struct avctp_server *server = l->data;
+ GSList *s;
- if (handler->id == id) {
- control_handlers = g_slist_remove(control_handlers,
- handler);
- g_free(handler);
- return TRUE;
+ for (s = server->sessions; s; s = s->next) {
+ struct avctp *session = s->data;
+ struct avctp_channel *control = session->control;
+ GSList *h;
+
+ if (control == NULL)
+ continue;
+
+ for (h = control->handlers; h; h = h->next) {
+ struct avctp_pdu_handler *handler = h->data;
+
+ if (handler->id != id)
+ continue;
+
+ control->handlers = g_slist_remove(
+ control->handlers,
+ handler);
+ g_free(handler);
+ return TRUE;
+ }
}
}
@@ -1284,12 +1301,37 @@ gboolean avctp_unregister_pdu_handler(unsigned int id)
gboolean avctp_unregister_browsing_pdu_handler(unsigned int id)
{
- if (browsing_handler->id != id)
- return FALSE;
+ GSList *l;
- g_free(browsing_handler);
- browsing_handler = NULL;
- return TRUE;
+ for (l = servers; l; l = l->next) {
+ struct avctp_server *server = l->data;
+ GSList *s;
+
+ for (s = server->sessions; s; s = s->next) {
+ struct avctp *session = l->data;
+ struct avctp_channel *browsing = session->browsing;
+ GSList *h;
+
+ if (browsing == NULL)
+ continue;
+
+ for (h = browsing->handlers; h; h = h->next) {
+ struct avctp_browsing_pdu_handler *handler =
+ h->data;
+
+ if (handler->id != id)
+ continue;
+
+ browsing->handlers = g_slist_remove(
+ browsing->handlers,
+ handler);
+ g_free(handler);
+ return TRUE;
+ }
+ }
+ }
+
+ return FALSE;
}
struct avctp *avctp_connect(const bdaddr_t *src, const bdaddr_t *dst)
diff --git a/audio/avctp.h b/audio/avctp.h
index 6d39a20..41cf6c5 100644
--- a/audio/avctp.h
+++ b/audio/avctp.h
@@ -98,12 +98,14 @@ struct avctp *avctp_get(const bdaddr_t *src, const bdaddr_t *dst);
int avctp_connect_browsing(struct avctp *session);
void avctp_disconnect(struct avctp *session);
-unsigned int avctp_register_pdu_handler(uint8_t opcode, avctp_control_pdu_cb cb,
- void *user_data);
+unsigned int avctp_register_pdu_handler(struct avctp *session, uint8_t opcode,
+ avctp_control_pdu_cb cb,
+ void *user_data);
gboolean avctp_unregister_pdu_handler(unsigned int id);
-unsigned int avctp_register_browsing_pdu_handler(avctp_browsing_pdu_cb cb,
- void *user_data);
+unsigned int avctp_register_browsing_pdu_handler(struct avctp *session,
+ avctp_browsing_pdu_cb cb,
+ void *user_data);
gboolean avctp_unregister_browsing_pdu_handler(unsigned int id);
int avctp_send_passthrough(struct avctp *session, uint8_t op);
diff --git a/audio/avrcp.c b/audio/avrcp.c
index 1ed6a24..f31372b 100644
--- a/audio/avrcp.c
+++ b/audio/avrcp.c
@@ -184,8 +184,8 @@ struct avrcp {
uint16_t version;
int features;
- unsigned int control_handler;
- unsigned int browsing_handler;
+ unsigned int control_id;
+ unsigned int browsing_id;
uint16_t registered_events;
uint8_t transaction;
uint8_t transaction_events[AVRCP_EVENT_LAST + 1];
@@ -1228,6 +1228,19 @@ static struct browsing_pdu_handler {
{ },
};
+size_t avrcp_browsing_general_reject(uint8_t *operands)
+{
+ struct avrcp_browsing_header *pdu = (void *) operands;
+ uint8_t status;
+
+ pdu->pdu_id = AVRCP_GENERAL_REJECT;
+ status = AVRCP_STATUS_INVALID_COMMAND;
+
+ pdu->param_len = htons(sizeof(status));
+ memcpy(pdu->params, &status, (sizeof(status)));
+ return AVRCP_BROWSING_HEADER_LENGTH + sizeof(status);
+}
+
static size_t handle_browsing_pdu(struct avctp *conn,
uint8_t transaction, uint8_t *operands,
size_t operand_count, void *user_data)
@@ -1235,7 +1248,6 @@ static size_t handle_browsing_pdu(struct avctp *conn,
struct avrcp *session = user_data;
struct browsing_pdu_handler *handler;
struct avrcp_browsing_header *pdu = (void *) operands;
- uint8_t status;
DBG("AVRCP Browsing PDU 0x%02X, len 0x%04X", pdu->pdu_id,
pdu->param_len);
@@ -1245,20 +1257,12 @@ static size_t handle_browsing_pdu(struct avctp *conn,
break;
}
- if (handler == NULL || handler->func == NULL) {
- pdu->pdu_id = AVRCP_GENERAL_REJECT;
- status = AVRCP_STATUS_INVALID_COMMAND;
- goto err;
- }
+ if (handler == NULL || handler->func == NULL)
+ return avrcp_browsing_general_reject(operands);
session->transaction = transaction;
handler->func(session, pdu, transaction);
return AVRCP_BROWSING_HEADER_LENGTH + ntohs(pdu->param_len);
-
-err:
- pdu->param_len = htons(sizeof(status));
- memcpy(pdu->params, &status, (sizeof(status)));
- return AVRCP_BROWSING_HEADER_LENGTH + sizeof(status);
}
size_t avrcp_handle_vendor_reject(uint8_t *code, uint8_t *operands)
@@ -1370,12 +1374,12 @@ static void state_changed(struct audio_device *dev, avctp_state_t old_state,
server->sessions = g_slist_remove(server->sessions, session);
- if (session->control_handler)
- avctp_unregister_pdu_handler(session->control_handler);
+ if (session->control_id > 0)
+ avctp_unregister_pdu_handler(session->control_id);
- if (session->browsing_handler)
+ if (session->browsing_id > 0)
avctp_unregister_browsing_pdu_handler(
- session->browsing_handler);
+ session->browsing_id);
if (session->player != NULL)
session->player->sessions = g_slist_remove(
@@ -1394,15 +1398,6 @@ static void state_changed(struct audio_device *dev, avctp_state_t old_state,
session->dev = dev;
session->player = g_slist_nth_data(server->players, 0);
- session->control_handler = avctp_register_pdu_handler(
- AVC_OP_VENDORDEP,
- handle_vendordep_pdu,
- session);
- session->browsing_handler =
- avctp_register_browsing_pdu_handler(
- handle_browsing_pdu,
- session);
-
server->sessions = g_slist_append(server->sessions, session);
rec = btd_device_get_record(dev->btd_dev, AVRCP_TARGET_UUID);
@@ -1429,6 +1424,15 @@ static void state_changed(struct audio_device *dev, avctp_state_t old_state,
if (session->features & AVRCP_FEATURE_BROWSING)
avctp_connect_browsing(session->conn);
}
+
+ session->control_id = avctp_register_pdu_handler(session->conn,
+ AVC_OP_VENDORDEP,
+ handle_vendordep_pdu,
+ session);
+ session->browsing_id = avctp_register_browsing_pdu_handler(
+ session->conn,
+ handle_browsing_pdu,
+ session);
default:
return;
}
diff --git a/audio/avrcp.h b/audio/avrcp.h
index 42d902b..6c651dd 100644
--- a/audio/avrcp.h
+++ b/audio/avrcp.h
@@ -105,3 +105,4 @@ void avrcp_player_event(struct avrcp_player *player, uint8_t id, void *data);
size_t avrcp_handle_vendor_reject(uint8_t *code, uint8_t *operands);
+size_t avrcp_browsing_general_reject(uint8_t *operands);
--
1.7.11.4
^ permalink raw reply related [flat|nested] 17+ messages in thread* [PATCH BlueZ 06/16] AVRCP: Simplify state_changed callback
2012-10-15 14:05 [PATCH BlueZ 01/16] AVCTP: Simplify channel handling Luiz Augusto von Dentz
` (3 preceding siblings ...)
2012-10-15 14:05 ` [PATCH BlueZ 05/16] AVRCP: Fix crash on disconnect Luiz Augusto von Dentz
@ 2012-10-15 14:05 ` Luiz Augusto von Dentz
2012-10-15 14:05 ` [PATCH BlueZ 07/16] AVCTP: Make use of allocate buffer to send data Luiz Augusto von Dentz
` (9 subsequent siblings)
14 siblings, 0 replies; 17+ messages in thread
From: Luiz Augusto von Dentz @ 2012-10-15 14:05 UTC (permalink / raw)
To: linux-bluetooth
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
Move session creation and destroy to their own functions
---
audio/avrcp.c | 91 ++++++++++++++++++++++++++++++++++-------------------------
1 file changed, 53 insertions(+), 38 deletions(-)
diff --git a/audio/avrcp.c b/audio/avrcp.c
index f31372b..7188039 100644
--- a/audio/avrcp.c
+++ b/audio/avrcp.c
@@ -1352,15 +1352,63 @@ static struct avrcp *find_session(GSList *list, struct audio_device *dev)
return NULL;
}
-static void state_changed(struct audio_device *dev, avctp_state_t old_state,
- avctp_state_t new_state, void *user_data)
+static struct avrcp *session_create(struct avrcp_server *server,
+ struct audio_device *dev)
{
- struct avrcp_server *server;
struct avrcp *session;
const sdp_record_t *rec;
sdp_list_t *list;
sdp_profile_desc_t *desc;
+ session = g_new0(struct avrcp, 1);
+ session->server = server;
+ session->conn = avctp_connect(&dev->src, &dev->dst);
+ session->dev = dev;
+ session->player = g_slist_nth_data(server->players, 0);
+
+ server->sessions = g_slist_append(server->sessions, session);
+
+ rec = btd_device_get_record(dev->btd_dev, AVRCP_TARGET_UUID);
+ if (rec == NULL)
+ return session;
+
+ if (sdp_get_profile_descs(rec, &list) < 0)
+ return session;
+
+ desc = list->data;
+ session->version = desc->version;
+ sdp_get_int_attr(rec, SDP_ATTR_SUPPORTED_FEATURES, &session->features);
+
+ sdp_list_free(list, free);
+
+ return session;
+}
+
+static void session_destroy(struct avrcp *session)
+{
+ struct avrcp_server *server = session->server;
+ struct avrcp_player *player = session->player;
+
+ server->sessions = g_slist_remove(server->sessions, session);
+
+ if (session->control_id > 0)
+ avctp_unregister_pdu_handler(session->control_id);
+
+ if (session->browsing_id > 0)
+ avctp_unregister_browsing_pdu_handler(session->browsing_id);
+
+ if (player != NULL)
+ player->sessions = g_slist_remove(player->sessions, session);
+
+ g_free(session);
+}
+
+static void state_changed(struct audio_device *dev, avctp_state_t old_state,
+ avctp_state_t new_state, void *user_data)
+{
+ struct avrcp_server *server;
+ struct avrcp *session;
+
server = find_server(servers, &dev->src);
if (!server)
return;
@@ -1372,47 +1420,14 @@ static void state_changed(struct audio_device *dev, avctp_state_t old_state,
if (session == NULL)
break;
- server->sessions = g_slist_remove(server->sessions, session);
+ session_destroy(session);
- if (session->control_id > 0)
- avctp_unregister_pdu_handler(session->control_id);
-
- if (session->browsing_id > 0)
- avctp_unregister_browsing_pdu_handler(
- session->browsing_id);
-
- if (session->player != NULL)
- session->player->sessions = g_slist_remove(
- session->player->sessions,
- session);
-
- g_free(session);
break;
case AVCTP_STATE_CONNECTING:
if (session != NULL)
break;
- session = g_new0(struct avrcp, 1);
- session->server = server;
- session->conn = avctp_connect(&dev->src, &dev->dst);
- session->dev = dev;
- session->player = g_slist_nth_data(server->players, 0);
-
- server->sessions = g_slist_append(server->sessions, session);
-
- rec = btd_device_get_record(dev->btd_dev, AVRCP_TARGET_UUID);
- if (rec == NULL)
- return;
-
- if (sdp_get_profile_descs(rec, &list) < 0)
- return;
-
- desc = list->data;
- session->version = desc->version;
- sdp_get_int_attr(rec, SDP_ATTR_SUPPORTED_FEATURES,
- &session->features);
-
- sdp_list_free(list, free);
+ session_create(server, dev);
break;
case AVCTP_STATE_CONNECTED:
--
1.7.11.4
^ permalink raw reply related [flat|nested] 17+ messages in thread* [PATCH BlueZ 07/16] AVCTP: Make use of allocate buffer to send data
2012-10-15 14:05 [PATCH BlueZ 01/16] AVCTP: Simplify channel handling Luiz Augusto von Dentz
` (4 preceding siblings ...)
2012-10-15 14:05 ` [PATCH BlueZ 06/16] AVRCP: Simplify state_changed callback Luiz Augusto von Dentz
@ 2012-10-15 14:05 ` Luiz Augusto von Dentz
2012-10-15 14:05 ` [PATCH BlueZ 08/16] AVCTP: Wait confirmation to send button release Luiz Augusto von Dentz
` (8 subsequent siblings)
14 siblings, 0 replies; 17+ messages in thread
From: Luiz Augusto von Dentz @ 2012-10-15 14:05 UTC (permalink / raw)
To: linux-bluetooth
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
There is no need to use the stack as the channel now have proper buffer
which can be used to store data to be send.
In addition to that make avctp_send_passthrough to use avctp_send.
---
audio/avctp.c | 83 ++++++++++++++++++++++++-----------------------------------
1 file changed, 34 insertions(+), 49 deletions(-)
diff --git a/audio/avctp.c b/audio/avctp.c
index 228d5b7..9a62bc0 100644
--- a/audio/avctp.c
+++ b/audio/avctp.c
@@ -1066,51 +1066,11 @@ void avctp_unregister(const bdaddr_t *src)
g_free(server);
}
-int avctp_send_passthrough(struct avctp *session, uint8_t op)
-{
- unsigned char buf[AVCTP_HEADER_LENGTH + AVC_HEADER_LENGTH + 2];
- struct avctp_header *avctp = (void *) buf;
- struct avc_header *avc = (void *) &buf[AVCTP_HEADER_LENGTH];
- uint8_t *operands = &buf[AVCTP_HEADER_LENGTH + AVC_HEADER_LENGTH];
- int sk;
-
- if (session->state != AVCTP_STATE_CONNECTED)
- return -ENOTCONN;
-
- memset(buf, 0, sizeof(buf));
-
- avctp->transaction = id++;
- avctp->packet_type = AVCTP_PACKET_SINGLE;
- avctp->cr = AVCTP_COMMAND;
- avctp->pid = htons(AV_REMOTE_SVCLASS_ID);
-
- avc->code = AVC_CTYPE_CONTROL;
- avc->subunit_type = AVC_SUBUNIT_PANEL;
- avc->opcode = AVC_OP_PASSTHROUGH;
-
- operands[0] = op & 0x7f;
- operands[1] = 0;
-
- sk = g_io_channel_unix_get_fd(session->control->io);
-
- if (write(sk, buf, sizeof(buf)) < 0)
- return -errno;
-
- /* Button release */
- avctp->transaction = id++;
- operands[0] |= 0x80;
-
- if (write(sk, buf, sizeof(buf)) < 0)
- return -errno;
-
- return 0;
-}
-
static int avctp_send(struct avctp *session, uint8_t transaction, uint8_t cr,
uint8_t code, uint8_t subunit, uint8_t opcode,
uint8_t *operands, size_t operand_count)
{
- uint8_t buf[AVCTP_HEADER_LENGTH + AVC_HEADER_LENGTH];
+ struct avctp_channel *control = session->control;
struct avctp_header *avctp;
struct avc_header *avc;
struct msghdr msg;
@@ -1120,12 +1080,20 @@ static int avctp_send(struct avctp *session, uint8_t transaction, uint8_t cr,
if (session->state != AVCTP_STATE_CONNECTED)
return -ENOTCONN;
+ iov[0].iov_base = control->buffer;
+ iov[0].iov_len = sizeof(*avctp) + sizeof(*avc);
+ iov[1].iov_base = operands;
+ iov[1].iov_len = operand_count;
+
+ if (control->omtu < (iov[0].iov_len + iov[1].iov_len))
+ return -EOVERFLOW;
+
sk = g_io_channel_unix_get_fd(session->control->io);
- memset(buf, 0, sizeof(buf));
+ memset(control->buffer, 0, iov[0].iov_len);
- avctp = (void *) buf;
- avc = (void *) &buf[AVCTP_HEADER_LENGTH];
+ avctp = (void *) control->buffer;
+ avc = (void *) avctp + sizeof(*avctp);
avctp->transaction = transaction;
avctp->packet_type = AVCTP_PACKET_SINGLE;
@@ -1136,11 +1104,6 @@ static int avctp_send(struct avctp *session, uint8_t transaction, uint8_t cr,
avc->subunit_type = subunit;
avc->opcode = opcode;
- iov[0].iov_base = buf;
- iov[0].iov_len = sizeof(buf);
- iov[1].iov_base = operands;
- iov[1].iov_len = operand_count;
-
memset(&msg, 0, sizeof(msg));
msg.msg_iov = iov;
msg.msg_iovlen = 2;
@@ -1151,6 +1114,28 @@ static int avctp_send(struct avctp *session, uint8_t transaction, uint8_t cr,
return err;
}
+int avctp_send_passthrough(struct avctp *session, uint8_t op)
+{
+ uint8_t operands[2];
+ int ret;
+
+ operands[0] = op & 0x7f;
+ operands[1] = 0;
+
+ ret = avctp_send(session, id, AVCTP_COMMAND, AVC_CTYPE_CONTROL,
+ AVC_SUBUNIT_PANEL, AVC_OP_PASSTHROUGH,
+ operands, sizeof(operands));
+ if (ret < 0)
+ return ret;
+
+ /* Button release */
+ operands[0] |= 0x80;
+
+ return avctp_send(session, id, AVCTP_COMMAND, AVC_CTYPE_CONTROL,
+ AVC_SUBUNIT_PANEL, AVC_OP_PASSTHROUGH,
+ operands, sizeof(operands));
+}
+
int avctp_send_vendordep(struct avctp *session, uint8_t transaction,
uint8_t code, uint8_t subunit,
uint8_t *operands, size_t operand_count)
--
1.7.11.4
^ permalink raw reply related [flat|nested] 17+ messages in thread* [PATCH BlueZ 08/16] AVCTP: Wait confirmation to send button release
2012-10-15 14:05 [PATCH BlueZ 01/16] AVCTP: Simplify channel handling Luiz Augusto von Dentz
` (5 preceding siblings ...)
2012-10-15 14:05 ` [PATCH BlueZ 07/16] AVCTP: Make use of allocate buffer to send data Luiz Augusto von Dentz
@ 2012-10-15 14:05 ` Luiz Augusto von Dentz
2012-10-15 14:05 ` [PATCH BlueZ 09/16] AVCTP: Add proper prefix to AVC passthrough codes Luiz Augusto von Dentz
` (7 subsequent siblings)
14 siblings, 0 replies; 17+ messages in thread
From: Luiz Augusto von Dentz @ 2012-10-15 14:05 UTC (permalink / raw)
To: linux-bluetooth
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
---
audio/avctp.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++------------
1 file changed, 46 insertions(+), 12 deletions(-)
diff --git a/audio/avctp.c b/audio/avctp.c
index 9a62bc0..c72d2d5 100644
--- a/audio/avctp.c
+++ b/audio/avctp.c
@@ -1114,26 +1114,60 @@ static int avctp_send(struct avctp *session, uint8_t transaction, uint8_t cr,
return err;
}
-int avctp_send_passthrough(struct avctp *session, uint8_t op)
+static int avctp_send_req(struct avctp *session, uint8_t code,
+ uint8_t subunit, uint8_t opcode,
+ uint8_t *operands, size_t operand_count,
+ avctp_rsp_cb func, void *user_data)
{
- uint8_t operands[2];
- int ret;
+ struct avctp_rsp_handler *handler;
+ int err;
- operands[0] = op & 0x7f;
- operands[1] = 0;
+ err = avctp_send(session, id, AVCTP_COMMAND, code, subunit,
+ opcode, operands, operand_count);
+ if (err < 0)
+ return err;
- ret = avctp_send(session, id, AVCTP_COMMAND, AVC_CTYPE_CONTROL,
- AVC_SUBUNIT_PANEL, AVC_OP_PASSTHROUGH,
- operands, sizeof(operands));
- if (ret < 0)
- return ret;
+ handler = g_new0(struct avctp_rsp_handler, 1);
+ handler->id = id;
+ handler->func = func;
+ handler->user_data = user_data;
+
+ session->handlers = g_slist_prepend(session->handlers, handler);
+
+ id++;
+
+ return 0;
+}
+
+static gboolean avctp_passthrough_rsp(struct avctp *session, uint8_t code,
+ uint8_t subunit, uint8_t *operands,
+ size_t operand_count, void *user_data)
+{
+ if (code != AVC_CTYPE_ACCEPTED)
+ return FALSE;
/* Button release */
operands[0] |= 0x80;
- return avctp_send(session, id, AVCTP_COMMAND, AVC_CTYPE_CONTROL,
+ avctp_send(session, id, AVCTP_COMMAND, AVC_CTYPE_CONTROL,
AVC_SUBUNIT_PANEL, AVC_OP_PASSTHROUGH,
- operands, sizeof(operands));
+ operands, sizeof(operand_count));
+
+ return FALSE;
+}
+
+int avctp_send_passthrough(struct avctp *session, uint8_t op)
+{
+ uint8_t operands[2];
+
+ /* Button pressed */
+ operands[0] = op & 0x7f;
+ operands[1] = 0;
+
+ return avctp_send_req(session, AVC_CTYPE_CONTROL,
+ AVC_SUBUNIT_PANEL, AVC_OP_PASSTHROUGH,
+ operands, sizeof(operands),
+ avctp_passthrough_rsp, NULL);
}
int avctp_send_vendordep(struct avctp *session, uint8_t transaction,
--
1.7.11.4
^ permalink raw reply related [flat|nested] 17+ messages in thread* [PATCH BlueZ 09/16] AVCTP: Add proper prefix to AVC passthrough codes
2012-10-15 14:05 [PATCH BlueZ 01/16] AVCTP: Simplify channel handling Luiz Augusto von Dentz
` (6 preceding siblings ...)
2012-10-15 14:05 ` [PATCH BlueZ 08/16] AVCTP: Wait confirmation to send button release Luiz Augusto von Dentz
@ 2012-10-15 14:05 ` Luiz Augusto von Dentz
2012-10-15 14:05 ` [PATCH BlueZ 10/16] AVCTP: Add proper queueing for channels Luiz Augusto von Dentz
` (6 subsequent siblings)
14 siblings, 0 replies; 17+ messages in thread
From: Luiz Augusto von Dentz @ 2012-10-15 14:05 UTC (permalink / raw)
To: linux-bluetooth
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
---
audio/avctp.c | 22 +++++++++++-----------
audio/avctp.h | 24 ++++++++++++------------
audio/control.c | 4 ++--
3 files changed, 25 insertions(+), 25 deletions(-)
diff --git a/audio/avctp.c b/audio/avctp.c
index c72d2d5..b982a3b 100644
--- a/audio/avctp.c
+++ b/audio/avctp.c
@@ -178,13 +178,13 @@ static struct {
uint8_t avc;
uint16_t uinput;
} key_map[] = {
- { "PLAY", PLAY_OP, KEY_PLAYCD },
- { "STOP", STAVC_OP_OP, KEY_STOPCD },
- { "PAUSE", PAUSE_OP, KEY_PAUSECD },
- { "FORWARD", FORWARD_OP, KEY_NEXTSONG },
- { "BACKWARD", BACKWARD_OP, KEY_PREVIOUSSONG },
- { "REWIND", REWIND_OP, KEY_REWIND },
- { "FAST FORWARD", FAST_FORWARD_OP, KEY_FASTFORWARD },
+ { "PLAY", AVC_PLAY, KEY_PLAYCD },
+ { "STOP", AVC_STOP, KEY_STOPCD },
+ { "PAUSE", AVC_PAUSE, KEY_PAUSECD },
+ { "FORWARD", AVC_FORWARD, KEY_NEXTSONG },
+ { "BACKWARD", AVC_BACKWARD, KEY_PREVIOUSSONG },
+ { "REWIND", AVC_REWIND, KEY_REWIND },
+ { "FAST FORWARD", AVC_FAST_FORWARD, KEY_FASTFORWARD },
{ NULL }
};
@@ -677,10 +677,10 @@ static void init_uinput(struct avctp *session)
device_get_name(dev->btd_dev, name, sizeof(name));
if (g_str_equal(name, "Nokia CK-20W")) {
- session->key_quirks[FORWARD_OP] |= QUIRK_NO_RELEASE;
- session->key_quirks[BACKWARD_OP] |= QUIRK_NO_RELEASE;
- session->key_quirks[PLAY_OP] |= QUIRK_NO_RELEASE;
- session->key_quirks[PAUSE_OP] |= QUIRK_NO_RELEASE;
+ session->key_quirks[AVC_FORWARD] |= QUIRK_NO_RELEASE;
+ session->key_quirks[AVC_BACKWARD] |= QUIRK_NO_RELEASE;
+ session->key_quirks[AVC_PLAY] |= QUIRK_NO_RELEASE;
+ session->key_quirks[AVC_PAUSE] |= QUIRK_NO_RELEASE;
}
ba2str(&session->dst, address);
diff --git a/audio/avctp.h b/audio/avctp.h
index 41cf6c5..c00a3fc 100644
--- a/audio/avctp.h
+++ b/audio/avctp.h
@@ -49,18 +49,18 @@
#define AVC_SUBUNIT_PANEL 0x09
/* operands in passthrough commands */
-#define VOL_UP_OP 0x41
-#define VOL_DOWN_OP 0x42
-#define MUTE_OP 0x43
-#define PLAY_OP 0x44
-#define STAVC_OP_OP 0x45
-#define PAUSE_OP 0x46
-#define RECORD_OP 0x47
-#define REWIND_OP 0x48
-#define FAST_FORWARD_OP 0x49
-#define EJECT_OP 0x4a
-#define FORWARD_OP 0x4b
-#define BACKWARD_OP 0x4c
+#define AVC_VOLUME_UP 0x41
+#define AVC_VOLUME_DOWN 0x42
+#define AVC_MUTE 0x43
+#define AVC_PLAY 0x44
+#define AVC_STOP 0x45
+#define AVC_PAUSE 0x46
+#define AVC_RECORD 0x47
+#define AVC_REWIND 0x48
+#define AVC_FAST_FORWARD 0x49
+#define AVC_EJECT 0x4a
+#define AVC_FORWARD 0x4b
+#define AVC_BACKWARD 0x4c
struct avctp;
diff --git a/audio/control.c b/audio/control.c
index 5ec8ca3..926bdfb 100644
--- a/audio/control.c
+++ b/audio/control.c
@@ -136,7 +136,7 @@ static DBusMessage *volume_up(DBusConnection *conn, DBusMessage *msg,
if (!control->target)
return btd_error_not_supported(msg);
- err = avctp_send_passthrough(control->session, VOL_UP_OP);
+ err = avctp_send_passthrough(control->session, AVC_VOLUME_UP);
if (err < 0)
return btd_error_failed(msg, strerror(-err));
@@ -156,7 +156,7 @@ static DBusMessage *volume_down(DBusConnection *conn, DBusMessage *msg,
if (!control->target)
return btd_error_not_supported(msg);
- err = avctp_send_passthrough(control->session, VOL_DOWN_OP);
+ err = avctp_send_passthrough(control->session, AVC_VOLUME_DOWN);
if (err < 0)
return btd_error_failed(msg, strerror(-err));
--
1.7.11.4
^ permalink raw reply related [flat|nested] 17+ messages in thread* [PATCH BlueZ 10/16] AVCTP: Add proper queueing for channels
2012-10-15 14:05 [PATCH BlueZ 01/16] AVCTP: Simplify channel handling Luiz Augusto von Dentz
` (7 preceding siblings ...)
2012-10-15 14:05 ` [PATCH BlueZ 09/16] AVCTP: Add proper prefix to AVC passthrough codes Luiz Augusto von Dentz
@ 2012-10-15 14:05 ` Luiz Augusto von Dentz
2012-10-15 14:05 ` [PATCH BlueZ 11/16] AVRCP: Fix reading wrong profile when acting as a controller Luiz Augusto von Dentz
` (5 subsequent siblings)
14 siblings, 0 replies; 17+ messages in thread
From: Luiz Augusto von Dentz @ 2012-10-15 14:05 UTC (permalink / raw)
To: linux-bluetooth
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
Add a request queue to channels to avoid dispatching too many requests
at once as the number of transaction is quite limited (16).
---
audio/avctp.c | 343 +++++++++++++++++++++++++++++++++++++++++-----------------
1 file changed, 245 insertions(+), 98 deletions(-)
diff --git a/audio/avctp.c b/audio/avctp.c
index b982a3b..0db6031 100644
--- a/audio/avctp.c
+++ b/audio/avctp.c
@@ -125,19 +125,40 @@ struct avctp_server {
GSList *sessions;
};
-struct avctp_rsp_handler {
- uint8_t id;
+struct avctp_control_req {
+ struct avctp_pending_req *p;
+ uint8_t code;
+ uint8_t subunit;
+ uint8_t op;
+ uint8_t *operands;
+ uint16_t operand_count;
avctp_rsp_cb func;
void *user_data;
};
+typedef int (*avctp_process_cb) (void *data);
+
+struct avctp_pending_req {
+ struct avctp_channel *chan;
+ uint8_t transaction;
+ guint timeout;
+ avctp_process_cb process;
+ void *data;
+ GDestroyNotify destroy;
+};
+
struct avctp_channel {
+ struct avctp *session;
GIOChannel *io;
guint watch;
uint16_t imtu;
uint16_t omtu;
uint8_t *buffer;
GSList *handlers;
+ struct avctp_pending_req *p;
+ GQueue *queue;
+ GSList *processed;
+ guint process_id;
};
struct avctp {
@@ -157,7 +178,6 @@ struct avctp {
struct avctp_channel *browsing;
uint8_t key_quirks[256];
- GSList *handlers;
};
struct avctp_pdu_handler {
@@ -190,9 +210,9 @@ static struct {
static GSList *callbacks = NULL;
static GSList *servers = NULL;
-static uint8_t id = 0;
static void auth_cb(DBusError *derr, void *user_data);
+static gboolean process_queue(gpointer user_data);
static int send_event(int fd, uint16_t type, uint16_t code, int32_t value)
{
@@ -339,6 +359,19 @@ static struct avctp_pdu_handler *find_handler(GSList *list, uint8_t opcode)
return NULL;
}
+static void pending_destroy(void *data)
+{
+ struct avctp_pending_req *req = data;
+
+ if (req->destroy)
+ req->destroy(req->data);
+
+ if (req->timeout > 0)
+ g_source_remove(req->timeout);
+
+ g_free(req);
+}
+
static void avctp_channel_destroy(struct avctp_channel *chan)
{
g_io_channel_shutdown(chan->io, TRUE, NULL);
@@ -347,7 +380,12 @@ static void avctp_channel_destroy(struct avctp_channel *chan)
if (chan->watch)
g_source_remove(chan->watch);
+ if (chan->process_id > 0)
+ g_source_remove(chan->process_id);
+
g_free(chan->buffer);
+ g_queue_free_full(chan->queue, pending_destroy);
+ g_slist_free_full(chan->processed, pending_destroy);
g_slist_free_full(chan->handlers, g_free);
g_free(chan);
}
@@ -383,7 +421,6 @@ static void avctp_disconnected(struct avctp *session)
server = session->server;
server->sessions = g_slist_remove(server->sessions, session);
- g_slist_free_full(session->handlers, g_free);
g_free(session);
}
@@ -423,26 +460,157 @@ static void avctp_set_state(struct avctp *session, avctp_state_t new_state)
}
}
-static void control_response(struct avctp *session, struct avctp_header *avctp,
- struct avc_header *avc, uint8_t *operands,
- size_t operand_count)
+static int avctp_send(struct avctp_channel *control, uint8_t transaction,
+ uint8_t cr, uint8_t code,
+ uint8_t subunit, uint8_t opcode,
+ uint8_t *operands, size_t operand_count)
+{
+ struct avctp_header *avctp;
+ struct avc_header *avc;
+ struct msghdr msg;
+ struct iovec iov[2];
+ int sk, err = 0;
+
+ DBG("transaction %u", transaction);
+
+ iov[0].iov_base = control->buffer;
+ iov[0].iov_len = sizeof(*avctp) + sizeof(*avc);
+ iov[1].iov_base = operands;
+ iov[1].iov_len = operand_count;
+
+ if (control->omtu < (iov[0].iov_len + iov[1].iov_len))
+ return -EOVERFLOW;
+
+ sk = g_io_channel_unix_get_fd(control->io);
+
+ memset(control->buffer, 0, iov[0].iov_len);
+
+ avctp = (void *) control->buffer;
+ avc = (void *) avctp + sizeof(*avctp);
+
+ avctp->transaction = transaction;
+ avctp->packet_type = AVCTP_PACKET_SINGLE;
+ avctp->cr = cr;
+ avctp->pid = htons(AV_REMOTE_SVCLASS_ID);
+
+ avc->code = code;
+ avc->subunit_type = subunit;
+ avc->opcode = opcode;
+
+ memset(&msg, 0, sizeof(msg));
+ msg.msg_iov = iov;
+ msg.msg_iovlen = 2;
+
+ if (sendmsg(sk, &msg, 0) < 0)
+ err = -errno;
+
+ return err;
+}
+
+static void control_req_destroy(void *data)
+{
+ struct avctp_control_req *req = data;
+
+ g_free(req->operands);
+ g_free(req);
+}
+
+static gboolean req_timeout(gpointer user_data)
+{
+ struct avctp_channel *chan = user_data;
+ struct avctp_pending_req *p = chan->p;
+
+ DBG("transaction %u", p->transaction);
+
+ p->timeout = 0;
+
+ pending_destroy(p);
+ chan->p = NULL;
+
+ if (chan->process_id == 0)
+ chan->process_id = g_idle_add(process_queue, chan);
+
+ return FALSE;
+}
+
+static int process_control(void *data)
+{
+ struct avctp_control_req *req = data;
+ struct avctp_pending_req *p = req->p;
+
+ return avctp_send(p->chan, p->transaction, AVCTP_COMMAND, req->code,
+ req->subunit, req->op,
+ req->operands, req->operand_count);
+}
+
+static gboolean process_queue(void *user_data)
+{
+ struct avctp_channel *chan = user_data;
+ struct avctp_pending_req *p = chan->p;
+
+ chan->process_id = 0;
+
+ if (p != NULL)
+ return FALSE;
+
+ while ((p = g_queue_pop_head(chan->queue))) {
+
+ if (p->process(p->data) == 0)
+ break;
+
+ pending_destroy(p);
+ }
+
+ if (p == NULL)
+ return FALSE;
+
+ chan->p = p;
+ p->timeout = g_timeout_add_seconds(2, req_timeout, chan);
+
+ return FALSE;
+
+}
+
+static void control_response(struct avctp_channel *control,
+ struct avctp_header *avctp,
+ struct avc_header *avc,
+ uint8_t *operands,
+ size_t operand_count)
{
+ struct avctp_pending_req *p = control->p;
+ struct avctp_control_req *req = p->data;
GSList *l;
- for (l = session->handlers; l; l = l->next) {
- struct avctp_rsp_handler *handler = l->data;
+ if (p && p->transaction == avctp->transaction) {
+ control->processed = g_slist_prepend(control->processed, p);
+
+ if (p->timeout > 0) {
+ g_source_remove(p->timeout);
+ p->timeout = 0;
+ }
+
+ control->p = NULL;
+
+ if (control->process_id == 0)
+ control->process_id = g_idle_add(process_queue,
+ control);
+ }
+
+ for (l = control->processed; l; l = l->next) {
+ p = l->data;
+ req = p->data;
- if (handler->id != avctp->transaction)
+ if (p->transaction != avctp->transaction)
continue;
- if (handler->func && handler->func(session, avc->code,
+ if (req->func && req->func(control->session, avc->code,
avc->subunit_type,
operands, operand_count,
- handler->user_data))
+ req->user_data))
return;
- session->handlers = g_slist_remove(session->handlers, handler);
- g_free(handler);
+ control->processed = g_slist_remove(control->processed, p);
+ pending_destroy(p);
return;
}
@@ -564,7 +732,7 @@ static gboolean session_cb(GIOChannel *chan, GIOCondition cond,
avc->opcode, operand_count);
if (avctp->cr == AVCTP_RESPONSE) {
- control_response(session, avctp, avc, operands, operand_count);
+ control_response(control, avctp, avc, operands, operand_count);
return TRUE;
}
@@ -692,12 +860,15 @@ static void init_uinput(struct avctp *session)
DBG("AVRCP: uinput initialized for %s", address);
}
-static struct avctp_channel *avctp_channel_create(GIOChannel *io)
+static struct avctp_channel *avctp_channel_create(struct avctp *session,
+ GIOChannel *io)
{
struct avctp_channel *chan;
chan = g_new0(struct avctp_channel, 1);
+ chan->session = session;
chan->io = g_io_channel_ref(io);
+ chan->queue = g_queue_new();
return chan;
}
@@ -731,7 +902,7 @@ static void avctp_connect_browsing_cb(GIOChannel *chan, GError *err,
DBG("AVCTP Browsing: connected to %s", address);
if (session->browsing == NULL)
- session->browsing = avctp_channel_create(chan);
+ session->browsing = avctp_channel_create(session, chan);
session->browsing->imtu = imtu;
session->browsing->omtu = omtu;
@@ -777,7 +948,7 @@ static void avctp_connect_cb(GIOChannel *chan, GError *err, gpointer data)
DBG("AVCTP: connected to %s", address);
if (session->control == NULL)
- session->control = avctp_channel_create(chan);
+ session->control = avctp_channel_create(session, chan);
session->control->imtu = imtu;
session->control->omtu = omtu;
@@ -894,7 +1065,7 @@ static void avctp_control_confirm(struct avctp *session, GIOChannel *chan,
}
avctp_set_state(session, AVCTP_STATE_CONNECTING);
- session->control = avctp_channel_create(chan);
+ session->control = avctp_channel_create(session, chan);
session->auth_id = btd_request_authorization(&dev->src, &dev->dst,
AVRCP_TARGET_UUID,
@@ -1066,52 +1237,25 @@ void avctp_unregister(const bdaddr_t *src)
g_free(server);
}
-static int avctp_send(struct avctp *session, uint8_t transaction, uint8_t cr,
- uint8_t code, uint8_t subunit, uint8_t opcode,
- uint8_t *operands, size_t operand_count)
+static struct avctp_pending_req *pending_create(struct avctp_channel *chan,
+ avctp_process_cb process,
+ void *data,
+ GDestroyNotify destroy)
{
- struct avctp_channel *control = session->control;
- struct avctp_header *avctp;
- struct avc_header *avc;
- struct msghdr msg;
- struct iovec iov[2];
- int sk, err = 0;
-
- if (session->state != AVCTP_STATE_CONNECTED)
- return -ENOTCONN;
-
- iov[0].iov_base = control->buffer;
- iov[0].iov_len = sizeof(*avctp) + sizeof(*avc);
- iov[1].iov_base = operands;
- iov[1].iov_len = operand_count;
-
- if (control->omtu < (iov[0].iov_len + iov[1].iov_len))
- return -EOVERFLOW;
-
- sk = g_io_channel_unix_get_fd(session->control->io);
-
- memset(control->buffer, 0, iov[0].iov_len);
-
- avctp = (void *) control->buffer;
- avc = (void *) avctp + sizeof(*avctp);
-
- avctp->transaction = transaction;
- avctp->packet_type = AVCTP_PACKET_SINGLE;
- avctp->cr = cr;
- avctp->pid = htons(AV_REMOTE_SVCLASS_ID);
-
- avc->code = code;
- avc->subunit_type = subunit;
- avc->opcode = opcode;
+ struct avctp_pending_req *p;
+ static uint8_t transaction = 0;
- memset(&msg, 0, sizeof(msg));
- msg.msg_iov = iov;
- msg.msg_iovlen = 2;
+ p = g_new0(struct avctp_pending_req, 1);
+ p->chan = chan;
+ p->transaction = transaction;
+ p->process = process;
+ p->data = data;
+ p->destroy = destroy;
- if (sendmsg(sk, &msg, 0) < 0)
- err = -errno;
+ transaction++;
+ transaction %= 16;
- return err;
+ return p;
}
static int avctp_send_req(struct avctp *session, uint8_t code,
@@ -1119,22 +1263,30 @@ static int avctp_send_req(struct avctp *session, uint8_t code,
uint8_t *operands, size_t operand_count,
avctp_rsp_cb func, void *user_data)
{
- struct avctp_rsp_handler *handler;
- int err;
+ struct avctp_channel *control = session->control;
+ struct avctp_pending_req *p;
+ struct avctp_control_req *req;
- err = avctp_send(session, id, AVCTP_COMMAND, code, subunit,
- opcode, operands, operand_count);
- if (err < 0)
- return err;
+ if (control == NULL)
+ return -ENOTCONN;
- handler = g_new0(struct avctp_rsp_handler, 1);
- handler->id = id;
- handler->func = func;
- handler->user_data = user_data;
+ req = g_new0(struct avctp_control_req, 1);
+ req->code = code;
+ req->subunit = subunit;
+ req->op = opcode;
+ req->func = func;
+ req->operands = g_memdup(operands, operand_count);
+ req->operand_count = operand_count;
+ req->user_data = user_data;
+
+ p = pending_create(control, process_control, req, control_req_destroy);
+
+ req->p = p;
- session->handlers = g_slist_prepend(session->handlers, handler);
+ g_queue_push_tail(control->queue, p);
- id++;
+ if (control->process_id == 0)
+ control->process_id = g_idle_add(process_queue, control);
return 0;
}
@@ -1143,15 +1295,18 @@ static gboolean avctp_passthrough_rsp(struct avctp *session, uint8_t code,
uint8_t subunit, uint8_t *operands,
size_t operand_count, void *user_data)
{
+ DBG("code %u", code);
+
if (code != AVC_CTYPE_ACCEPTED)
return FALSE;
/* Button release */
operands[0] |= 0x80;
- avctp_send(session, id, AVCTP_COMMAND, AVC_CTYPE_CONTROL,
- AVC_SUBUNIT_PANEL, AVC_OP_PASSTHROUGH,
- operands, sizeof(operand_count));
+ avctp_send_req(session, AVC_CTYPE_CONTROL,
+ AVC_SUBUNIT_PANEL, AVC_OP_PASSTHROUGH,
+ operands, operand_count,
+ NULL, NULL);
return FALSE;
}
@@ -1160,6 +1315,8 @@ int avctp_send_passthrough(struct avctp *session, uint8_t op)
{
uint8_t operands[2];
+ DBG("");
+
/* Button pressed */
operands[0] = op & 0x7f;
operands[1] = 0;
@@ -1174,7 +1331,12 @@ int avctp_send_vendordep(struct avctp *session, uint8_t transaction,
uint8_t code, uint8_t subunit,
uint8_t *operands, size_t operand_count)
{
- return avctp_send(session, transaction, AVCTP_RESPONSE, code, subunit,
+ struct avctp_channel *control = session->control;
+
+ if (control == NULL)
+ return -ENOTCONN;
+
+ return avctp_send(control, transaction, AVCTP_RESPONSE, code, subunit,
AVC_OP_VENDORDEP, operands, operand_count);
}
@@ -1183,24 +1345,9 @@ int avctp_send_vendordep_req(struct avctp *session, uint8_t code,
size_t operand_count,
avctp_rsp_cb func, void *user_data)
{
- struct avctp_rsp_handler *handler;
- int err;
-
- err = avctp_send(session, id, AVCTP_COMMAND, code, subunit,
- AVC_OP_VENDORDEP, operands, operand_count);
- if (err < 0)
- return err;
-
- handler = g_new0(struct avctp_rsp_handler, 1);
- handler->id = id;
- handler->func = func;
- handler->user_data = user_data;
-
- session->handlers = g_slist_prepend(session->handlers, handler);
-
- id++;
-
- return 0;
+ return avctp_send_req(session, code, subunit, AVC_OP_VENDORDEP,
+ operands, operand_count,
+ func, user_data);
}
unsigned int avctp_add_state_cb(avctp_state_cb cb, void *user_data)
@@ -1380,7 +1527,7 @@ struct avctp *avctp_connect(const bdaddr_t *src, const bdaddr_t *dst)
return NULL;
}
- session->control = avctp_channel_create(io);
+ session->control = avctp_channel_create(session, io);
g_io_channel_unref(io);
return session;
@@ -1409,7 +1556,7 @@ int avctp_connect_browsing(struct avctp *session)
return -EIO;
}
- session->browsing = avctp_channel_create(io);
+ session->browsing = avctp_channel_create(session, io);
g_io_channel_unref(io);
return 0;
--
1.7.11.4
^ permalink raw reply related [flat|nested] 17+ messages in thread* [PATCH BlueZ 11/16] AVRCP: Fix reading wrong profile when acting as a controller
2012-10-15 14:05 [PATCH BlueZ 01/16] AVCTP: Simplify channel handling Luiz Augusto von Dentz
` (8 preceding siblings ...)
2012-10-15 14:05 ` [PATCH BlueZ 10/16] AVCTP: Add proper queueing for channels Luiz Augusto von Dentz
@ 2012-10-15 14:05 ` Luiz Augusto von Dentz
2012-10-15 14:05 ` [PATCH BlueZ 12/16] AVRCP: Fix handling TG PDUs as they were CT PDUs Luiz Augusto von Dentz
` (4 subsequent siblings)
14 siblings, 0 replies; 17+ messages in thread
From: Luiz Augusto von Dentz @ 2012-10-15 14:05 UTC (permalink / raw)
To: linux-bluetooth
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
The role indicate which record should be read so we properly determine
the version and what features are supported.
---
audio/avrcp.c | 19 ++++++++++++++++++-
1 file changed, 18 insertions(+), 1 deletion(-)
diff --git a/audio/avrcp.c b/audio/avrcp.c
index 7188039..8abb426 100644
--- a/audio/avrcp.c
+++ b/audio/avrcp.c
@@ -58,6 +58,9 @@
#include "avrcp.h"
#include "sdpd.h"
#include "dbus-common.h"
+#include "control.h"
+#include "avdtp.h"
+#include "sink.h"
/* Company IDs for vendor dependent commands */
#define IEEEID_BTSIG 0x001958
@@ -181,6 +184,7 @@ struct avrcp {
struct avctp *conn;
struct audio_device *dev;
struct avrcp_player *player;
+ gboolean target;
uint16_t version;
int features;
@@ -1368,7 +1372,20 @@ static struct avrcp *session_create(struct avrcp_server *server,
server->sessions = g_slist_append(server->sessions, session);
- rec = btd_device_get_record(dev->btd_dev, AVRCP_TARGET_UUID);
+ if (dev->sink && !dev->source)
+ session->target = TRUE;
+ else if (dev->source && !dev->sink)
+ session->target = FALSE;
+ else if (dev->sink && sink_is_active(dev))
+ session->target = TRUE;
+ else
+ session->target = FALSE;
+
+ if (session->target)
+ rec = btd_device_get_record(dev->btd_dev, AVRCP_REMOTE_UUID);
+ else
+ rec = btd_device_get_record(dev->btd_dev, AVRCP_TARGET_UUID);
+
if (rec == NULL)
return session;
--
1.7.11.4
^ permalink raw reply related [flat|nested] 17+ messages in thread* [PATCH BlueZ 12/16] AVRCP: Fix handling TG PDUs as they were CT PDUs
2012-10-15 14:05 [PATCH BlueZ 01/16] AVCTP: Simplify channel handling Luiz Augusto von Dentz
` (9 preceding siblings ...)
2012-10-15 14:05 ` [PATCH BlueZ 11/16] AVRCP: Fix reading wrong profile when acting as a controller Luiz Augusto von Dentz
@ 2012-10-15 14:05 ` Luiz Augusto von Dentz
2012-10-15 14:05 ` [PATCH BlueZ 13/16] AVRCP: Add proper role init procedure Luiz Augusto von Dentz
` (3 subsequent siblings)
14 siblings, 0 replies; 17+ messages in thread
From: Luiz Augusto von Dentz @ 2012-10-15 14:05 UTC (permalink / raw)
To: linux-bluetooth
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
PDU handling needs to different depending on the acting role.
---
audio/avrcp.c | 81 +++++++++++++++++++++++++++++++++++++++++------------------
1 file changed, 57 insertions(+), 24 deletions(-)
diff --git a/audio/avrcp.c b/audio/avrcp.c
index 8abb426..85dd241 100644
--- a/audio/avrcp.c
+++ b/audio/avrcp.c
@@ -188,6 +188,10 @@ struct avrcp {
uint16_t version;
int features;
+ void (*init) (struct avrcp *session);
+
+ const struct control_pdu_handler *control_handlers;
+
unsigned int control_id;
unsigned int browsing_id;
uint16_t registered_events;
@@ -196,6 +200,13 @@ struct avrcp {
struct pending_pdu *pending_pdu;
};
+struct control_pdu_handler {
+ uint8_t pdu_id;
+ uint8_t code;
+ uint8_t (*func) (struct avrcp *session, struct avrcp_header *pdu,
+ uint8_t transaction);
+};
+
static GSList *servers = NULL;
static unsigned int avctp_id = 0;
@@ -1128,12 +1139,7 @@ err:
return AVC_CTYPE_REJECTED;
}
-static struct control_pdu_handler {
- uint8_t pdu_id;
- uint8_t code;
- uint8_t (*func) (struct avrcp *session, struct avrcp_header *pdu,
- uint8_t transaction);
-} control_handlers[] = {
+static const struct control_pdu_handler tg_control_handlers[] = {
{ AVRCP_GET_CAPABILITIES, AVC_CTYPE_STATUS,
avrcp_handle_get_capabilities },
{ AVRCP_LIST_PLAYER_ATTRIBUTES, AVC_CTYPE_STATUS,
@@ -1165,6 +1171,10 @@ static struct control_pdu_handler {
{ },
};
+static const struct control_pdu_handler ct_control_handlers[] = {
+ { },
+};
+
/* 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,
@@ -1172,7 +1182,7 @@ static size_t handle_vendordep_pdu(struct avctp *conn, uint8_t transaction,
void *user_data)
{
struct avrcp *session = user_data;
- struct control_pdu_handler *handler;
+ const struct control_pdu_handler *handler;
struct avrcp_header *pdu = (void *) operands;
uint32_t company_id = get_company_id(pdu->company_id);
@@ -1192,7 +1202,7 @@ static size_t handle_vendordep_pdu(struct avctp *conn, uint8_t transaction,
goto err_metadata;
}
- for (handler = control_handlers; handler->pdu_id; handler++) {
+ for (handler = session->control_handlers; handler->pdu_id; handler++) {
if (handler->pdu_id == pdu->pdu_id)
break;
}
@@ -1356,6 +1366,39 @@ static struct avrcp *find_session(GSList *list, struct audio_device *dev)
return NULL;
}
+static void session_tg_init(struct avrcp *session)
+{
+ struct avrcp_server *server = session->server;
+
+ session->player = g_slist_nth_data(server->players, 0);
+ session->control_handlers = tg_control_handlers;
+
+ if (session->version >= 0x0104) {
+ register_volume_notification(session);
+ if (session->features & AVRCP_FEATURE_BROWSING)
+ avctp_connect_browsing(session->conn);
+ }
+
+ session->control_id = avctp_register_pdu_handler(session->conn,
+ AVC_OP_VENDORDEP,
+ handle_vendordep_pdu,
+ session);
+ session->browsing_id = avctp_register_browsing_pdu_handler(
+ session->conn,
+ handle_browsing_pdu,
+ session);
+}
+
+static void session_ct_init(struct avrcp *session)
+{
+ session->control_handlers = ct_control_handlers;
+
+ session->control_id = avctp_register_pdu_handler(session->conn,
+ AVC_OP_VENDORDEP,
+ handle_vendordep_pdu,
+ session);
+}
+
static struct avrcp *session_create(struct avrcp_server *server,
struct audio_device *dev)
{
@@ -1368,7 +1411,6 @@ static struct avrcp *session_create(struct avrcp_server *server,
session->server = server;
session->conn = avctp_connect(&dev->src, &dev->dst);
session->dev = dev;
- session->player = g_slist_nth_data(server->players, 0);
server->sessions = g_slist_append(server->sessions, session);
@@ -1381,10 +1423,13 @@ static struct avrcp *session_create(struct avrcp_server *server,
else
session->target = FALSE;
- if (session->target)
+ if (session->target) {
+ session->init = session_tg_init;
rec = btd_device_get_record(dev->btd_dev, AVRCP_REMOTE_UUID);
- else
+ } else {
+ session->init = session_ct_init;
rec = btd_device_get_record(dev->btd_dev, AVRCP_TARGET_UUID);
+ }
if (rec == NULL)
return session;
@@ -1451,20 +1496,8 @@ static void state_changed(struct audio_device *dev, avctp_state_t old_state,
if (session == NULL)
break;
- if (session->version >= 0x0104) {
- register_volume_notification(session);
- if (session->features & AVRCP_FEATURE_BROWSING)
- avctp_connect_browsing(session->conn);
- }
+ session->init(session);
- session->control_id = avctp_register_pdu_handler(session->conn,
- AVC_OP_VENDORDEP,
- handle_vendordep_pdu,
- session);
- session->browsing_id = avctp_register_browsing_pdu_handler(
- session->conn,
- handle_browsing_pdu,
- session);
default:
return;
}
--
1.7.11.4
^ permalink raw reply related [flat|nested] 17+ messages in thread* [PATCH BlueZ 13/16] AVRCP: Add proper role init procedure
2012-10-15 14:05 [PATCH BlueZ 01/16] AVCTP: Simplify channel handling Luiz Augusto von Dentz
` (10 preceding siblings ...)
2012-10-15 14:05 ` [PATCH BlueZ 12/16] AVRCP: Fix handling TG PDUs as they were CT PDUs Luiz Augusto von Dentz
@ 2012-10-15 14:05 ` Luiz Augusto von Dentz
2012-10-15 14:05 ` [PATCH BlueZ 14/16] control: Add basic support for AVRCP 1.0 controller Luiz Augusto von Dentz
` (2 subsequent siblings)
14 siblings, 0 replies; 17+ messages in thread
From: Luiz Augusto von Dentz @ 2012-10-15 14:05 UTC (permalink / raw)
To: linux-bluetooth
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
---
audio/avrcp.c | 113 ++++++++++++++++++++++++++++++++++++++++++++++++++++------
1 file changed, 103 insertions(+), 10 deletions(-)
diff --git a/audio/avrcp.c b/audio/avrcp.c
index 85dd241..8336831 100644
--- a/audio/avrcp.c
+++ b/audio/avrcp.c
@@ -104,6 +104,7 @@
#define CAP_EVENTS_SUPPORTED 0x03
#define AVRCP_REGISTER_NOTIFICATION_PARAM_LENGTH 5
+#define AVRCP_GET_CAPABILITIES_PARAM_LENGTH 1
#define AVRCP_FEATURE_CATEGORY_1 0x0001
#define AVRCP_FEATURE_CATEGORY_2 0x0002
@@ -215,7 +216,7 @@ static uint32_t company_ids[] = {
IEEEID_BTSIG,
};
-static void register_volume_notification(struct avrcp *session);
+static void register_notification(struct avrcp *session, uint8_t event);
static sdp_record_t *avrcp_ct_record(void)
{
@@ -1306,7 +1307,7 @@ static struct avrcp_server *find_server(GSList *list, const bdaddr_t *src)
return NULL;
}
-static gboolean avrcp_handle_volume_changed(struct avctp *conn,
+static gboolean avrcp_handle_event(struct avctp *conn,
uint8_t code, uint8_t subunit,
uint8_t *operands, size_t operand_count,
void *user_data)
@@ -1314,26 +1315,34 @@ static gboolean avrcp_handle_volume_changed(struct avctp *conn,
struct avrcp *session = user_data;
struct avrcp_player *player = session->player;
struct avrcp_header *pdu = (void *) operands;
+ uint8_t event;
uint8_t volume;
if (code != AVC_CTYPE_INTERIM && code != AVC_CTYPE_CHANGED)
return FALSE;
- volume = pdu->params[1] & 0x7F;
+ event = pdu->params[0];
- if (player)
- player->cb->set_volume(volume, session->dev,
+ switch (event) {
+ case AVRCP_EVENT_VOLUME_CHANGED:
+ volume = pdu->params[1] & 0x7F;
+
+ if (player)
+ player->cb->set_volume(volume, session->dev,
player->user_data);
+ break;
+ }
+
if (code == AVC_CTYPE_CHANGED) {
- register_volume_notification(session);
+ register_notification(session, event);
return FALSE;
}
return TRUE;
}
-static void register_volume_notification(struct avrcp *session)
+static void register_notification(struct avrcp *session, uint8_t event)
{
uint8_t buf[AVRCP_HEADER_LENGTH + AVRCP_REGISTER_NOTIFICATION_PARAM_LENGTH];
struct avrcp_header *pdu = (void *) buf;
@@ -1344,14 +1353,89 @@ static void register_volume_notification(struct avrcp *session)
set_company_id(pdu->company_id, IEEEID_BTSIG);
pdu->pdu_id = AVRCP_REGISTER_NOTIFICATION;
pdu->packet_type = AVRCP_PACKET_TYPE_SINGLE;
- pdu->params[0] = AVRCP_EVENT_VOLUME_CHANGED;
+ pdu->params[0] = event;
pdu->params_len = htons(AVRCP_REGISTER_NOTIFICATION_PARAM_LENGTH);
length = AVRCP_HEADER_LENGTH + ntohs(pdu->params_len);
avctp_send_vendordep_req(session->conn, AVC_CTYPE_NOTIFY,
AVC_SUBUNIT_PANEL, buf, length,
- avrcp_handle_volume_changed, session);
+ avrcp_handle_event, session);
+}
+
+static gboolean avrcp_get_capabilities_resp(struct avctp *conn,
+ uint8_t code, uint8_t subunit,
+ uint8_t *operands, size_t operand_count,
+ void *user_data)
+{
+ struct avrcp *session = user_data;
+ struct avrcp_header *pdu = (void *) operands;
+ uint8_t count;
+
+ if (pdu->params[0] != CAP_EVENTS_SUPPORTED)
+ return FALSE;
+
+ count = pdu->params[1];
+
+ for (; count > 0; count--) {
+ uint8_t event = pdu->params[1 + count];
+
+ switch (event) {
+ case AVRCP_EVENT_STATUS_CHANGED:
+ case AVRCP_EVENT_TRACK_CHANGED:
+ register_notification(session, event);
+ break;
+ }
+ }
+
+ return FALSE;
+}
+
+static void avrcp_get_capabilities(struct avrcp *session)
+{
+ uint8_t buf[AVRCP_HEADER_LENGTH + AVRCP_GET_CAPABILITIES_PARAM_LENGTH];
+ struct avrcp_header *pdu = (void *) buf;
+ uint8_t length;
+
+ memset(buf, 0, sizeof(buf));
+
+ set_company_id(pdu->company_id, IEEEID_BTSIG);
+ pdu->pdu_id = AVRCP_GET_CAPABILITIES;
+ pdu->packet_type = AVRCP_PACKET_TYPE_SINGLE;
+ pdu->params[0] = CAP_EVENTS_SUPPORTED;
+ pdu->params_len = htons(AVRCP_GET_CAPABILITIES_PARAM_LENGTH);
+
+ length = AVRCP_HEADER_LENGTH + ntohs(pdu->params_len);
+
+ avctp_send_vendordep_req(session->conn, AVC_CTYPE_STATUS,
+ AVC_SUBUNIT_PANEL, buf, length,
+ avrcp_get_capabilities_resp,
+ session);
+}
+
+static gboolean avrcp_get_play_status_rsp(struct avctp *conn,
+ uint8_t code, uint8_t subunit,
+ uint8_t *operands, size_t operand_count,
+ void *user_data)
+{
+ return FALSE;
+}
+
+static void avrcp_get_play_status(struct avrcp *session)
+{
+ uint8_t buf[AVRCP_HEADER_LENGTH];
+ struct avrcp_header *pdu = (void *) buf;
+
+ memset(buf, 0, sizeof(buf));
+
+ set_company_id(pdu->company_id, IEEEID_BTSIG);
+ pdu->pdu_id = AVRCP_GET_PLAY_STATUS;
+ pdu->packet_type = AVRCP_PACKET_TYPE_SINGLE;
+
+ avctp_send_vendordep_req(session->conn, AVC_CTYPE_STATUS,
+ AVC_SUBUNIT_PANEL, buf, sizeof(buf),
+ avrcp_get_play_status_rsp,
+ session);
}
static struct avrcp *find_session(GSList *list, struct audio_device *dev)
@@ -1370,11 +1454,13 @@ static void session_tg_init(struct avrcp *session)
{
struct avrcp_server *server = session->server;
+ DBG("%p version 0x%04x", session, session->version);
+
session->player = g_slist_nth_data(server->players, 0);
session->control_handlers = tg_control_handlers;
if (session->version >= 0x0104) {
- register_volume_notification(session);
+ register_notification(session, AVRCP_EVENT_VOLUME_CHANGED);
if (session->features & AVRCP_FEATURE_BROWSING)
avctp_connect_browsing(session->conn);
}
@@ -1393,6 +1479,13 @@ static void session_ct_init(struct avrcp *session)
{
session->control_handlers = ct_control_handlers;
+ DBG("%p version 0x%04x", session, session->version);
+
+ if (session->version >= 0x0103) {
+ avrcp_get_capabilities(session);
+ avrcp_get_play_status(session);
+ }
+
session->control_id = avctp_register_pdu_handler(session->conn,
AVC_OP_VENDORDEP,
handle_vendordep_pdu,
--
1.7.11.4
^ permalink raw reply related [flat|nested] 17+ messages in thread* [PATCH BlueZ 14/16] control: Add basic support for AVRCP 1.0 controller
2012-10-15 14:05 [PATCH BlueZ 01/16] AVCTP: Simplify channel handling Luiz Augusto von Dentz
` (11 preceding siblings ...)
2012-10-15 14:05 ` [PATCH BlueZ 13/16] AVRCP: Add proper role init procedure Luiz Augusto von Dentz
@ 2012-10-15 14:05 ` Luiz Augusto von Dentz
2012-10-15 14:05 ` [PATCH BlueZ 15/16] control: Add Control.Connect API Luiz Augusto von Dentz
2012-10-15 14:05 ` [PATCH BlueZ 16/16] control: Add Control.Disconnect method Luiz Augusto von Dentz
14 siblings, 0 replies; 17+ messages in thread
From: Luiz Augusto von Dentz @ 2012-10-15 14:05 UTC (permalink / raw)
To: linux-bluetooth
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
---
audio/control.c | 61 ++++++++++++++++++++++++++++++++++++++---------------
doc/control-api.txt | 24 +++++++++++++++++++++
2 files changed, 68 insertions(+), 17 deletions(-)
diff --git a/audio/control.c b/audio/control.c
index 926bdfb..b77c4cb 100644
--- a/audio/control.c
+++ b/audio/control.c
@@ -123,8 +123,8 @@ static DBusMessage *control_is_connected(DBusConnection *conn,
return reply;
}
-static DBusMessage *volume_up(DBusConnection *conn, DBusMessage *msg,
- void *data)
+static DBusMessage *key_pressed(DBusConnection *conn, DBusMessage *msg,
+ uint8_t op, void *data)
{
struct audio_device *device = data;
struct control *control = device->control;
@@ -136,31 +136,53 @@ static DBusMessage *volume_up(DBusConnection *conn, DBusMessage *msg,
if (!control->target)
return btd_error_not_supported(msg);
- err = avctp_send_passthrough(control->session, AVC_VOLUME_UP);
+ err = avctp_send_passthrough(control->session, op);
if (err < 0)
return btd_error_failed(msg, strerror(-err));
return dbus_message_new_method_return(msg);
}
-static DBusMessage *volume_down(DBusConnection *conn, DBusMessage *msg,
+static DBusMessage *control_volume_up(DBusConnection *conn, DBusMessage *msg,
void *data)
{
- struct audio_device *device = data;
- struct control *control = device->control;
- int err;
+ return key_pressed(conn, msg, AVC_VOLUME_UP, data);
+}
- if (!control->session)
- return btd_error_not_connected(msg);
+static DBusMessage *control_volume_down(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ return key_pressed(conn, msg, AVC_VOLUME_DOWN, data);
+}
- if (!control->target)
- return btd_error_not_supported(msg);
+static DBusMessage *control_play(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ return key_pressed(conn, msg, AVC_PLAY, data);
+}
- err = avctp_send_passthrough(control->session, AVC_VOLUME_DOWN);
- if (err < 0)
- return btd_error_failed(msg, strerror(-err));
+static DBusMessage *control_pause(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ return key_pressed(conn, msg, AVC_PAUSE, data);
+}
- return dbus_message_new_method_return(msg);
+static DBusMessage *control_stop(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ return key_pressed(conn, msg, AVC_STOP, data);
+}
+
+static DBusMessage *control_next(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ return key_pressed(conn, msg, AVC_FORWARD, data);
+}
+
+static DBusMessage *control_previous(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ return key_pressed(conn, msg, AVC_BACKWARD, data);
}
static gboolean control_property_get_connected(
@@ -179,8 +201,13 @@ static const GDBusMethodTable control_methods[] = {
{ GDBUS_DEPRECATED_METHOD("IsConnected",
NULL, GDBUS_ARGS({ "connected", "b" }),
control_is_connected) },
- { GDBUS_METHOD("VolumeUp", NULL, NULL, volume_up) },
- { GDBUS_METHOD("VolumeDown", NULL, NULL, volume_down) },
+ { GDBUS_METHOD("Play", NULL, NULL, control_play) },
+ { GDBUS_METHOD("Pause", NULL, NULL, control_pause) },
+ { GDBUS_METHOD("Stop", NULL, NULL, control_stop) },
+ { GDBUS_METHOD("Next", NULL, NULL, control_next) },
+ { GDBUS_METHOD("Previous", NULL, NULL, control_previous) },
+ { GDBUS_METHOD("VolumeUp", NULL, NULL, control_volume_up) },
+ { GDBUS_METHOD("VolumeDown", NULL, NULL, control_volume_down) },
{ }
};
diff --git a/doc/control-api.txt b/doc/control-api.txt
index eacfbcd..3792dfa 100644
--- a/doc/control-api.txt
+++ b/doc/control-api.txt
@@ -21,6 +21,30 @@ Methods boolean IsConnected() {deprecated}
Returns all properties for the interface. See the
properties section for available properties.
+ void Play()
+
+ Resume playback.
+
+ void Pause()
+
+ Pause playback.
+
+ void Stop()
+
+ Stop playback.
+
+ void Next()
+
+ Next item.
+
+ void Previous()
+
+ Previous item.
+
+ void VolumeDown()
+
+ Adjust remote volume one step down
+
void VolumeUp()
Adjust remote volume one step up
--
1.7.11.4
^ permalink raw reply related [flat|nested] 17+ messages in thread* [PATCH BlueZ 15/16] control: Add Control.Connect API
2012-10-15 14:05 [PATCH BlueZ 01/16] AVCTP: Simplify channel handling Luiz Augusto von Dentz
` (12 preceding siblings ...)
2012-10-15 14:05 ` [PATCH BlueZ 14/16] control: Add basic support for AVRCP 1.0 controller Luiz Augusto von Dentz
@ 2012-10-15 14:05 ` Luiz Augusto von Dentz
2012-10-15 14:05 ` [PATCH BlueZ 16/16] control: Add Control.Disconnect method Luiz Augusto von Dentz
14 siblings, 0 replies; 17+ messages in thread
From: Luiz Augusto von Dentz @ 2012-10-15 14:05 UTC (permalink / raw)
To: linux-bluetooth
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
This method can be used to manually connect AVRCP when acting as a
controller.
---
audio/control.c | 45 +++++++++++++++++++++++++++++++++++++++++++++
doc/control-api.txt | 4 ++++
2 files changed, 49 insertions(+)
diff --git a/audio/control.c b/audio/control.c
index b77c4cb..e15e9ba 100644
--- a/audio/control.c
+++ b/audio/control.c
@@ -61,6 +61,7 @@ static unsigned int avctp_id = 0;
struct control {
struct avctp *session;
gboolean target;
+ DBusMessage *connect;
};
static void state_changed(struct audio_device *dev, avctp_state_t old_state,
@@ -73,6 +74,15 @@ static void state_changed(struct audio_device *dev, avctp_state_t old_state,
case AVCTP_STATE_DISCONNECTED:
control->session = NULL;
+ if (control->connect) {
+ DBusMessage *reply = btd_error_failed(control->connect,
+ "Unable to connect");
+
+ g_dbus_send_message(btd_get_dbus_connection(), reply);
+ dbus_message_unref(control->connect);
+ control->connect = NULL;
+ }
+
if (old_state != AVCTP_STATE_CONNECTED)
break;
@@ -91,6 +101,13 @@ static void state_changed(struct audio_device *dev, avctp_state_t old_state,
break;
case AVCTP_STATE_CONNECTED:
+ if (control->connect) {
+ g_dbus_send_reply(conn, control->connect,
+ DBUS_TYPE_INVALID);
+ dbus_message_unref(control->connect);
+ control->connect = NULL;
+ }
+
g_dbus_emit_signal(conn, dev->path,
AUDIO_CONTROL_INTERFACE, "Connected",
DBUS_TYPE_INVALID);
@@ -123,6 +140,30 @@ static DBusMessage *control_is_connected(DBusConnection *conn,
return reply;
}
+static DBusMessage *control_connect(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ struct audio_device *device = data;
+ struct control *control = device->control;
+
+ if (control->session)
+ return btd_error_already_connected(msg);
+
+ if (!control->target)
+ return btd_error_not_supported(msg);
+
+ if (control->connect)
+ return btd_error_in_progress(msg);
+
+ control->session = avctp_connect(&device->src, &device->dst);
+ if (!control->session)
+ return btd_error_failed(msg, "Unable to connect");
+
+ control->connect = dbus_message_ref(msg);
+
+ return NULL;
+}
+
static DBusMessage *key_pressed(DBusConnection *conn, DBusMessage *msg,
uint8_t op, void *data)
{
@@ -201,6 +242,7 @@ static const GDBusMethodTable control_methods[] = {
{ GDBUS_DEPRECATED_METHOD("IsConnected",
NULL, GDBUS_ARGS({ "connected", "b" }),
control_is_connected) },
+ { GDBUS_ASYNC_METHOD("Connect", NULL, NULL, control_connect) },
{ GDBUS_METHOD("Play", NULL, NULL, control_play) },
{ GDBUS_METHOD("Pause", NULL, NULL, control_pause) },
{ GDBUS_METHOD("Stop", NULL, NULL, control_stop) },
@@ -233,6 +275,9 @@ static void path_unregister(void *data)
if (control->session)
avctp_disconnect(control->session);
+ if (control->connect)
+ dbus_message_unref(control->connect);
+
g_free(control);
dev->control = NULL;
}
diff --git a/doc/control-api.txt b/doc/control-api.txt
index 3792dfa..61069ea 100644
--- a/doc/control-api.txt
+++ b/doc/control-api.txt
@@ -21,6 +21,10 @@ Methods boolean IsConnected() {deprecated}
Returns all properties for the interface. See the
properties section for available properties.
+ void Connect()
+
+ Connect to remote device.
+
void Play()
Resume playback.
--
1.7.11.4
^ permalink raw reply related [flat|nested] 17+ messages in thread* [PATCH BlueZ 16/16] control: Add Control.Disconnect method
2012-10-15 14:05 [PATCH BlueZ 01/16] AVCTP: Simplify channel handling Luiz Augusto von Dentz
` (13 preceding siblings ...)
2012-10-15 14:05 ` [PATCH BlueZ 15/16] control: Add Control.Connect API Luiz Augusto von Dentz
@ 2012-10-15 14:05 ` Luiz Augusto von Dentz
14 siblings, 0 replies; 17+ messages in thread
From: Luiz Augusto von Dentz @ 2012-10-15 14:05 UTC (permalink / raw)
To: linux-bluetooth
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
This method can be used to disconnect AVRCP.
---
audio/control.c | 31 +++++++++++++++++++++++++++++++
doc/control-api.txt | 4 ++++
2 files changed, 35 insertions(+)
diff --git a/audio/control.c b/audio/control.c
index e15e9ba..8bb085a 100644
--- a/audio/control.c
+++ b/audio/control.c
@@ -62,6 +62,7 @@ struct control {
struct avctp *session;
gboolean target;
DBusMessage *connect;
+ DBusMessage *disconnect;
};
static void state_changed(struct audio_device *dev, avctp_state_t old_state,
@@ -83,6 +84,13 @@ static void state_changed(struct audio_device *dev, avctp_state_t old_state,
control->connect = NULL;
}
+ if (control->disconnect) {
+ g_dbus_send_reply(conn, control->disconnect,
+ DBUS_TYPE_INVALID);
+ dbus_message_unref(control->disconnect);
+ control->disconnect = NULL;
+ }
+
if (old_state != AVCTP_STATE_CONNECTED)
break;
@@ -164,6 +172,25 @@ static DBusMessage *control_connect(DBusConnection *conn, DBusMessage *msg,
return NULL;
}
+static DBusMessage *control_disconnect(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ struct audio_device *device = data;
+ struct control *control = device->control;
+
+ if (!control->session)
+ return btd_error_not_connected(msg);
+
+ if (control->disconnect)
+ return btd_error_in_progress(msg);
+
+ avctp_disconnect(control->session);
+
+ control->disconnect = dbus_message_ref(msg);
+
+ return NULL;
+}
+
static DBusMessage *key_pressed(DBusConnection *conn, DBusMessage *msg,
uint8_t op, void *data)
{
@@ -243,6 +270,7 @@ static const GDBusMethodTable control_methods[] = {
NULL, GDBUS_ARGS({ "connected", "b" }),
control_is_connected) },
{ GDBUS_ASYNC_METHOD("Connect", NULL, NULL, control_connect) },
+ { GDBUS_ASYNC_METHOD("Disconnect", NULL, NULL, control_disconnect) },
{ GDBUS_METHOD("Play", NULL, NULL, control_play) },
{ GDBUS_METHOD("Pause", NULL, NULL, control_pause) },
{ GDBUS_METHOD("Stop", NULL, NULL, control_stop) },
@@ -278,6 +306,9 @@ static void path_unregister(void *data)
if (control->connect)
dbus_message_unref(control->connect);
+ if (control->disconnect)
+ dbus_message_unref(control->disconnect);
+
g_free(control);
dev->control = NULL;
}
diff --git a/doc/control-api.txt b/doc/control-api.txt
index 61069ea..5391a8a 100644
--- a/doc/control-api.txt
+++ b/doc/control-api.txt
@@ -25,6 +25,10 @@ Methods boolean IsConnected() {deprecated}
Connect to remote device.
+ void Disconnect()
+
+ Disconnect from remote device.
+
void Play()
Resume playback.
--
1.7.11.4
^ permalink raw reply related [flat|nested] 17+ messages in thread