Diff between 60e7ff608c9a676d924920464b9bbc7f9472ad20 and c70ebf7cb4261b40090b52f2cd49be03125f1966

Changed Files

File Additions Deletions Status
profiles/audio/avctp.c +139 -63 modified

Full Patch

diff --git a/profiles/audio/avctp.c b/profiles/audio/avctp.c
index 0d39581..c192e21 100644
--- a/profiles/audio/avctp.c
+++ b/profiles/audio/avctp.c
@@ -61,9 +61,12 @@
  */
 #define AVC_PRESS_TIMEOUT	2
 
-#define CONTROL_TIMEOUT		AVC_PRESS_TIMEOUT
+#define CONTROL_TIMEOUT		10
 #define BROWSING_TIMEOUT	10
 
+#define PASSTHROUGH_QUEUE	0
+#define CONTROL_QUEUE		1
+
 #define QUIRK_NO_RELEASE 1 << 0
 
 /* Message types */
@@ -154,7 +157,7 @@ struct avctp_browsing_req {
 typedef int (*avctp_process_cb) (void *data);
 
 struct avctp_pending_req {
-	struct avctp_channel *chan;
+	struct avctp_queue *queue;
 	uint8_t transaction;
 	guint timeout;
 	bool retry;
@@ -164,6 +167,13 @@ struct avctp_pending_req {
 	GDestroyNotify destroy;
 };
 
+struct avctp_queue {
+	struct avctp_channel *chan;
+	struct avctp_pending_req *p;
+	GQueue *queue;
+	guint process_id;
+};
+
 struct avctp_channel {
 	struct avctp *session;
 	GIOChannel *io;
@@ -173,10 +183,8 @@ struct avctp_channel {
 	uint16_t omtu;
 	uint8_t *buffer;
 	GSList *handlers;
-	struct avctp_pending_req *p;
-	GQueue *queue;
+	GSList *queues;
 	GSList *processed;
-	guint process_id;
 	GDestroyNotify destroy;
 };
 
@@ -514,6 +522,21 @@ static void pending_destroy(gpointer data, gpointer user_data)
 	g_free(req);
 }
 
+static void avctp_queue_destroy(void *data)
+{
+	struct avctp_queue *queue = data;
+
+	if (queue->process_id > 0)
+		g_source_remove(queue->process_id);
+
+	if (queue->p)
+		pending_destroy(queue->p, NULL);
+
+	g_queue_foreach(queue->queue, pending_destroy, NULL);
+	g_queue_free(queue->queue);
+	g_free(queue);
+}
+
 static void avctp_channel_destroy(struct avctp_channel *chan)
 {
 	g_io_channel_shutdown(chan->io, TRUE, NULL);
@@ -522,18 +545,11 @@ static void avctp_channel_destroy(struct avctp_channel *chan)
 	if (chan->watch)
 		g_source_remove(chan->watch);
 
-	if (chan->p)
-		pending_destroy(chan->p, NULL);
-
-	if (chan->process_id > 0)
-		g_source_remove(chan->process_id);
-
 	if (chan->destroy)
 		chan->destroy(chan);
 
 	g_free(chan->buffer);
-	g_queue_foreach(chan->queue, pending_destroy, NULL);
-	g_queue_free(chan->queue);
+	g_slist_free_full(chan->queues, avctp_queue_destroy);
 	g_slist_foreach(chan->processed, pending_destroy, NULL);
 	g_slist_free(chan->processed);
 	g_slist_free_full(chan->handlers, g_free);
@@ -700,10 +716,11 @@ static int avctp_send(struct avctp_channel *control, uint8_t transaction,
 	return err;
 }
 
-static int avctp_browsing_send(struct avctp_channel *browsing,
+static int avctp_browsing_send(struct avctp_queue *queue,
 				uint8_t transaction, uint8_t cr,
 				uint8_t *operands, size_t operand_count)
 {
+	struct avctp_channel *browsing = queue->chan;
 	struct avctp_header *avctp;
 	struct msghdr msg;
 	struct iovec iov[2];
@@ -742,7 +759,7 @@ static void control_req_destroy(void *data)
 {
 	struct avctp_control_req *req = data;
 	struct avctp_pending_req *p = req->p;
-	struct avctp *session = p->chan->session;
+	struct avctp *session = p->queue->chan->session;
 
 	if (p->err == 0 || req->func == NULL)
 		goto done;
@@ -759,7 +776,7 @@ static void browsing_req_destroy(void *data)
 {
 	struct avctp_browsing_req *req = data;
 	struct avctp_pending_req *p = req->p;
-	struct avctp *session = p->chan->session;
+	struct avctp *session = p->queue->chan->session;
 
 	if (p->err == 0 || req->func == NULL)
 		goto done;
@@ -773,8 +790,8 @@ done:
 
 static gboolean req_timeout(gpointer user_data)
 {
-	struct avctp_channel *chan = user_data;
-	struct avctp_pending_req *p = chan->p;
+	struct avctp_queue *queue = user_data;
+	struct avctp_pending_req *p = queue->p;
 
 	DBG("transaction %u retry %s", p->transaction, p->retry ? "true" :
 								"false");
@@ -789,31 +806,48 @@ static gboolean req_timeout(gpointer user_data)
 	p->err = -ETIMEDOUT;
 
 	pending_destroy(p, NULL);
-	chan->p = NULL;
+	queue->p = NULL;
 
-	if (chan->process_id == 0)
-		chan->process_id = g_idle_add(process_queue, chan);
+	if (queue->process_id == 0)
+		queue->process_id = g_idle_add(process_queue, queue);
 
 	return FALSE;
 }
 
+static int process_passthrough(void *data)
+{
+	struct avctp_control_req *req = data;
+	struct avctp_pending_req *p = req->p;
+	int ret;
+
+	ret = avctp_send(p->queue->chan, p->transaction, AVCTP_COMMAND,
+			req->code, req->subunit, req->op, req->operands,
+			req->operand_count);
+	if (ret < 0)
+		return ret;
+
+	p->timeout = g_timeout_add_seconds(AVC_PRESS_TIMEOUT, req_timeout,
+								p->queue);
+
+	return 0;
+}
+
 static int process_control(void *data)
 {
 	struct avctp_control_req *req = data;
 	struct avctp_pending_req *p = req->p;
 	int ret;
 
-	ret = avctp_send(p->chan, p->transaction, AVCTP_COMMAND, req->code,
-			req->subunit, req->op, req->operands,
+	ret = avctp_send(p->queue->chan, p->transaction, AVCTP_COMMAND,
+			req->code, req->subunit, req->op, req->operands,
 			req->operand_count);
 	if (ret < 0)
 		return ret;
 
-	if (req->op != AVC_OP_PASSTHROUGH)
-		p->retry = !p->retry;
+	p->retry = !p->retry;
 
 	p->timeout = g_timeout_add_seconds(CONTROL_TIMEOUT, req_timeout,
-								p->chan);
+								p->queue);
 
 	return 0;
 }
@@ -824,28 +858,28 @@ static int process_browsing(void *data)
 	struct avctp_pending_req *p = req->p;
 	int ret;
 
-	ret = avctp_browsing_send(p->chan, p->transaction, AVCTP_COMMAND,
+	ret = avctp_browsing_send(p->queue, p->transaction, AVCTP_COMMAND,
 					req->operands, req->operand_count);
 	if (ret < 0)
 		return ret;
 
 	p->timeout = g_timeout_add_seconds(BROWSING_TIMEOUT, req_timeout,
-								p->chan);
+								p->queue);
 
 	return 0;
 }
 
 static gboolean process_queue(void *user_data)
 {
-	struct avctp_channel *chan = user_data;
-	struct avctp_pending_req *p = chan->p;
+	struct avctp_queue *queue = user_data;
+	struct avctp_pending_req *p = queue->p;
 
-	chan->process_id = 0;
+	queue->process_id = 0;
 
 	if (p != NULL)
 		return FALSE;
 
-	while ((p = g_queue_pop_head(chan->queue))) {
+	while ((p = g_queue_pop_head(queue->queue))) {
 
 		if (p->process(p->data) == 0)
 			break;
@@ -856,7 +890,7 @@ static gboolean process_queue(void *user_data)
 	if (p == NULL)
 		return FALSE;
 
-	chan->p = p;
+	queue->p = p;
 
 	return FALSE;
 
@@ -868,10 +902,18 @@ static void control_response(struct avctp_channel *control,
 					uint8_t *operands,
 					size_t operand_count)
 {
-	struct avctp_pending_req *p = control->p;
+	struct avctp_pending_req *p;
 	struct avctp_control_req *req;
+	struct avctp_queue *queue;
 	GSList *l;
 
+	if (avc->opcode == AVC_OP_PASSTHROUGH)
+		queue = g_slist_nth_data(control->queues, PASSTHROUGH_QUEUE);
+	else
+		queue = g_slist_nth_data(control->queues, CONTROL_QUEUE);
+
+	p = queue->p;
+
 	if (p && p->transaction == avctp->transaction) {
 		req = p->data;
 		if (req->op != avc->opcode)
@@ -884,11 +926,10 @@ static void control_response(struct avctp_channel *control,
 			p->timeout = 0;
 		}
 
-		control->p = NULL;
+		queue->p = NULL;
 
-		if (control->process_id == 0)
-			control->process_id = g_idle_add(process_queue,
-								control);
+		if (queue->process_id == 0)
+			queue->process_id = g_idle_add(process_queue, queue);
 	}
 
 done:
@@ -920,10 +961,15 @@ static void browsing_response(struct avctp_channel *browsing,
 					uint8_t *operands,
 					size_t operand_count)
 {
-	struct avctp_pending_req *p = browsing->p;
+	struct avctp_pending_req *p;
 	struct avctp_browsing_req *req;
+	struct avctp_queue *queue;
 	GSList *l;
 
+	queue = g_slist_nth_data(browsing->queues, 0);
+
+	p = queue->p;
+
 	if (p && p->transaction == avctp->transaction) {
 		browsing->processed = g_slist_prepend(browsing->processed, p);
 
@@ -932,11 +978,10 @@ static void browsing_response(struct avctp_channel *browsing,
 			p->timeout = 0;
 		}
 
-		browsing->p = NULL;
+		queue->p = NULL;
 
-		if (browsing->process_id == 0)
-			browsing->process_id = g_idle_add(process_queue,
-								browsing);
+		if (queue->process_id == 0)
+			queue->process_id = g_idle_add(process_queue, queue);
 	}
 
 	for (l = browsing->processed; l; l = l->next) {
@@ -1193,8 +1238,20 @@ static void init_uinput(struct avctp *session)
 		DBG("AVRCP: uinput initialized for %s", address);
 }
 
+static struct avctp_queue *avctp_queue_create(struct avctp_channel *chan)
+{
+	struct avctp_queue *queue;
+
+	queue = g_new0(struct avctp_queue, 1);
+	queue->chan = chan;
+	queue->queue = g_queue_new();
+
+	return queue;
+}
+
 static struct avctp_channel *avctp_channel_create(struct avctp *session,
 							GIOChannel *io,
+							int queues,
 							GDestroyNotify destroy)
 {
 	struct avctp_channel *chan;
@@ -1202,9 +1259,15 @@ static struct avctp_channel *avctp_channel_create(struct avctp *session,
 	chan = g_new0(struct avctp_channel, 1);
 	chan->session = session;
 	chan->io = g_io_channel_ref(io);
-	chan->queue = g_queue_new();
 	chan->destroy = destroy;
 
+	while (queues--) {
+		struct avctp_queue *queue;
+
+		queue = avctp_queue_create(chan);
+		chan->queues = g_slist_prepend(chan->queues, queue);
+	}
+
 	return chan;
 }
 
@@ -1232,6 +1295,7 @@ static void avctp_connect_browsing_cb(GIOChannel *chan, GError *err,
 {
 	struct avctp *session = data;
 	struct avctp_channel *browsing = session->browsing;
+	struct avctp_queue *queue;
 	char address[18];
 	uint16_t imtu, omtu;
 	GError *gerr = NULL;
@@ -1257,7 +1321,7 @@ static void avctp_connect_browsing_cb(GIOChannel *chan, GError *err,
 	DBG("AVCTP Browsing: connected to %s", address);
 
 	if (browsing == NULL) {
-		browsing = avctp_channel_create(session, chan,
+		browsing = avctp_channel_create(session, chan, 1,
 						avctp_destroy_browsing);
 		session->browsing = browsing;
 	}
@@ -1272,8 +1336,9 @@ static void avctp_connect_browsing_cb(GIOChannel *chan, GError *err,
 	avctp_set_state(session, AVCTP_STATE_BROWSING_CONNECTED, 0);
 
 	/* Process any request that was pending the connection to complete */
-	if (browsing->process_id == 0 && !g_queue_is_empty(browsing->queue))
-		browsing->process_id = g_idle_add(process_queue, browsing);
+	queue = g_slist_nth_data(browsing->queues, 0);
+	if (queue->process_id == 0 && !g_queue_is_empty(queue->queue))
+		queue->process_id = g_idle_add(process_queue, queue);
 
 	return;
 
@@ -1314,7 +1379,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(session, chan, NULL);
+		session->control = avctp_channel_create(session, chan, 2, NULL);
 
 	session->control->imtu = imtu;
 	session->control->omtu = omtu;
@@ -1436,7 +1501,7 @@ static void avctp_control_confirm(struct avctp *session, GIOChannel *chan,
 	}
 
 	avctp_set_state(session, AVCTP_STATE_CONNECTING, 0);
-	session->control = avctp_channel_create(session, chan, NULL);
+	session->control = avctp_channel_create(session, chan, 2, NULL);
 
 	src = btd_adapter_get_address(device_get_adapter(dev));
 	dst = device_get_address(dev);
@@ -1606,7 +1671,7 @@ void avctp_unregister(struct btd_adapter *adapter)
 	g_free(server);
 }
 
-static struct avctp_pending_req *pending_create(struct avctp_channel *chan,
+static struct avctp_pending_req *pending_create(struct avctp_queue *queue,
 						avctp_process_cb process,
 						void *data,
 						GDestroyNotify destroy)
@@ -1614,8 +1679,8 @@ static struct avctp_pending_req *pending_create(struct avctp_channel *chan,
 	struct avctp_pending_req *p;
 
 	p = g_new0(struct avctp_pending_req, 1);
-	p->chan = chan;
-	p->transaction = chan_get_transaction(chan);
+	p->queue = queue;
+	p->transaction = chan_get_transaction(queue->chan);
 	p->process = process;
 	p->data = data;
 	p->destroy = destroy;
@@ -1629,6 +1694,7 @@ static int avctp_send_req(struct avctp *session, uint8_t code,
 				avctp_rsp_cb func, void *user_data)
 {
 	struct avctp_channel *control = session->control;
+	struct avctp_queue *queue;
 	struct avctp_pending_req *p;
 	struct avctp_control_req *req;
 
@@ -1649,14 +1715,22 @@ static int avctp_send_req(struct avctp *session, uint8_t code,
 	req->operand_count = operand_count;
 	req->user_data = user_data;
 
-	p = pending_create(control, process_control, req, control_req_destroy);
+	if (opcode == AVC_OP_PASSTHROUGH) {
+		queue = g_slist_nth_data(control->queues, PASSTHROUGH_QUEUE);
+		p = pending_create(queue, process_passthrough, req,
+					control_req_destroy);
+	} else {
+		queue = g_slist_nth_data(control->queues, CONTROL_QUEUE);
+		p = pending_create(queue, process_control, req,
+					control_req_destroy);
+	}
 
 	req->p = p;
 
-	g_queue_push_tail(control->queue, p);
+	g_queue_push_tail(queue->queue, p);
 
-	if (control->process_id == 0)
-		control->process_id = g_idle_add(process_queue, control);
+	if (queue->process_id == 0)
+		queue->process_id = g_idle_add(process_queue, queue);
 
 	return 0;
 }
@@ -1666,6 +1740,7 @@ int avctp_send_browsing_req(struct avctp *session,
 				avctp_browsing_rsp_cb func, void *user_data)
 {
 	struct avctp_channel *browsing = session->browsing;
+	struct avctp_queue *queue;
 	struct avctp_pending_req *p;
 	struct avctp_browsing_req *req;
 
@@ -1678,19 +1753,20 @@ int avctp_send_browsing_req(struct avctp *session,
 	req->operand_count = operand_count;
 	req->user_data = user_data;
 
-	p = pending_create(browsing, process_browsing, req,
-			browsing_req_destroy);
+	queue = g_slist_nth_data(browsing->queues, 0);
+
+	p = pending_create(queue, process_browsing, req, browsing_req_destroy);
 
 	req->p = p;
 
-	g_queue_push_tail(browsing->queue, p);
+	g_queue_push_tail(queue->queue, p);
 
 	/* Connection did not complete, delay process of the request */
 	if (browsing->watch == 0)
 		return 0;
 
-	if (browsing->process_id == 0)
-		browsing->process_id = g_idle_add(process_queue, browsing);
+	if (queue->process_id == 0)
+		queue->process_id = g_idle_add(process_queue, queue);
 
 	return 0;
 }
@@ -2058,7 +2134,7 @@ struct avctp *avctp_connect(struct btd_device *device)
 		return NULL;
 	}
 
-	session->control = avctp_channel_create(session, io, NULL);
+	session->control = avctp_channel_create(session, io, 2, NULL);
 	session->initiator = true;
 	g_io_channel_unref(io);
 
@@ -2095,7 +2171,7 @@ int avctp_connect_browsing(struct avctp *session)
 		return -EIO;
 	}
 
-	session->browsing = avctp_channel_create(session, io,
+	session->browsing = avctp_channel_create(session, io, 1,
 						avctp_destroy_browsing);
 	g_io_channel_unref(io);