[PATCH BlueZ 10/16] AVCTP: Add proper queueing for channels

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



From: Luiz Augusto von Dentz <luiz.von.dentz@xxxxxxxxx>

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

--
To unsubscribe from this list: send the line "unsubscribe linux-bluetooth" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Bluez Devel]     [Linux Wireless Networking]     [Linux Wireless Personal Area Networking]     [Linux ATH6KL]     [Linux USB Devel]     [Linux Media Drivers]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Big List of Linux Books]

  Powered by Linux