From: Luiz Augusto von Dentz <luiz.dentz@gmail.com>
To: linux-bluetooth@vger.kernel.org
Subject: [PATCH BlueZ 05/16] AVRCP: Fix crash on disconnect
Date: Mon, 15 Oct 2012 16:05:25 +0200 [thread overview]
Message-ID: <1350309936-31588-5-git-send-email-luiz.dentz@gmail.com> (raw)
In-Reply-To: <1350309936-31588-1-git-send-email-luiz.dentz@gmail.com>
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
next prev parent reply other threads:[~2012-10-15 14:05 UTC|newest]
Thread overview: 17+ messages / expand[flat|nested] mbox.gz Atom feed top
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 ` [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
2012-10-15 14:05 ` Luiz Augusto von Dentz [this message]
2012-10-15 14:05 ` [PATCH BlueZ 06/16] AVRCP: Simplify state_changed callback 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
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 ` [PATCH BlueZ 09/16] AVCTP: Add proper prefix to AVC passthrough codes Luiz Augusto von Dentz
2012-10-15 14:05 ` [PATCH BlueZ 10/16] AVCTP: Add proper queueing for channels 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
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 ` [PATCH BlueZ 13/16] AVRCP: Add proper role init procedure 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
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
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1350309936-31588-5-git-send-email-luiz.dentz@gmail.com \
--to=luiz.dentz@gmail.com \
--cc=linux-bluetooth@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox