[PATCH] avctp: Add functionality for CT browsing commands

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

 



Add the missing functionality for full support of browsing channel
on the CT side. This is required for AVRCP >= 1.4
The patch is based on the control channel functionality

---
 profiles/audio/avctp.c | 140 +++++++++++++++++++++++++++++++++++++++++++++++++
 profiles/audio/avctp.h |   6 +++
 2 files changed, 146 insertions(+)

diff --git a/profiles/audio/avctp.c b/profiles/audio/avctp.c
index e65594d..02bee2b 100644
--- a/profiles/audio/avctp.c
+++ b/profiles/audio/avctp.c
@@ -136,6 +136,14 @@ struct avctp_control_req {
 	void *user_data;
 };
 
+struct avctp_browsing_req {
+	struct avctp_pending_req *p;
+	uint8_t *operands;
+	uint16_t operand_count;
+	avctp_browsing_rsp_cb func;
+	void *user_data;
+};
+
 typedef int (*avctp_process_cb) (void *data);
 
 struct avctp_pending_req {
@@ -527,6 +535,44 @@ static int avctp_send(struct avctp_channel *control, uint8_t transaction,
 	return err;
 }
 
+static int avctp_browsing_send(struct avctp_channel *browsing,
+				uint8_t transaction, uint8_t cr,
+				uint8_t *operands, size_t operand_count)
+{
+	struct avctp_header *avctp;
+	struct msghdr msg;
+	struct iovec iov[2];
+	int sk, err = 0;
+
+	iov[0].iov_base = browsing->buffer;
+	iov[0].iov_len  = sizeof(*avctp);
+	iov[1].iov_base = operands;
+	iov[1].iov_len  = operand_count;
+
+	if (browsing->omtu < (iov[0].iov_len + iov[1].iov_len))
+		return -EOVERFLOW;
+
+	sk = g_io_channel_unix_get_fd(browsing->io);
+
+	memset(browsing->buffer, 0, iov[0].iov_len);
+
+	avctp = (void *) browsing->buffer;
+
+	avctp->transaction = transaction;
+	avctp->packet_type = AVCTP_PACKET_SINGLE;
+	avctp->cr = cr;
+	avctp->pid = htons(AV_REMOTE_SVCLASS_ID);
+
+	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;
@@ -535,6 +581,14 @@ static void control_req_destroy(void *data)
 	g_free(req);
 }
 
+static void browsing_req_destroy(void *data)
+{
+	struct avctp_browsing_req *req = data;
+
+	g_free(req->operands);
+	g_free(req);
+}
+
 static gboolean req_timeout(gpointer user_data)
 {
 	struct avctp_channel *chan = user_data;
@@ -563,6 +617,15 @@ static int process_control(void *data)
 					req->operands, req->operand_count);
 }
 
+static int process_browsing(void *data)
+{
+	struct avctp_browsing_req *req = data;
+	struct avctp_pending_req *p = req->p;
+
+	return avctp_browsing_send(p->chan, p->transaction, AVCTP_COMMAND,
+					req->operands, req->operand_count);
+}
+
 static gboolean process_queue(void *user_data)
 {
 	struct avctp_channel *chan = user_data;
@@ -636,6 +699,48 @@ static void control_response(struct avctp_channel *control,
 	}
 }
 
+static void browsing_response(struct avctp_channel *browsing,
+					struct avctp_header *avctp,
+					uint8_t *operands,
+					size_t operand_count)
+{
+	struct avctp_pending_req *p = browsing->p;
+	struct avctp_browsing_req *req;
+	GSList *l;
+
+	if (p && p->transaction == avctp->transaction) {
+		browsing->processed = g_slist_prepend(browsing->processed, p);
+
+		if (p->timeout > 0) {
+			g_source_remove(p->timeout);
+			p->timeout = 0;
+		}
+
+		browsing->p = NULL;
+
+		if (browsing->process_id == 0)
+			browsing->process_id = g_idle_add(process_queue,
+								browsing);
+	}
+
+	for (l = browsing->processed; l; l = l->next) {
+		p = l->data;
+		req = p->data;
+
+		if (p->transaction != avctp->transaction)
+			continue;
+
+		if (req->func && req->func(browsing->session,
+						operands, operand_count,
+						req->user_data))
+			return;
+
+		browsing->processed = g_slist_remove(browsing->processed, p);
+
+		return;
+	}
+}
+
 static gboolean session_browsing_cb(GIOChannel *chan, GIOCondition cond,
 				gpointer data)
 {
@@ -665,6 +770,11 @@ static gboolean session_browsing_cb(GIOChannel *chan, GIOCondition cond,
 	ret -= AVCTP_HEADER_LENGTH;
 	operand_count = ret;
 
+	if (avctp->cr == AVCTP_RESPONSE) {
+		browsing_response(browsing, avctp, operands, operand_count);
+		return TRUE;
+	}
+
 	packet_size = AVCTP_HEADER_LENGTH;
 	avctp->cr = AVCTP_RESPONSE;
 
@@ -1294,6 +1404,36 @@ static int avctp_send_req(struct avctp *session, uint8_t code,
 	return 0;
 }
 
+int avctp_send_browsing_req(struct avctp *session,
+				uint8_t *operands, size_t operand_count,
+				avctp_browsing_rsp_cb func, void *user_data)
+{
+	struct avctp_channel *browsing = session->browsing;
+	struct avctp_pending_req *p;
+	struct avctp_browsing_req *req;
+
+	if (browsing == NULL)
+		return -ENOTCONN;
+
+	req = g_new0(struct avctp_browsing_req, 1);
+	req->func = func;
+	req->operands = g_memdup(operands, operand_count);
+	req->operand_count = operand_count;
+	req->user_data = user_data;
+
+	p = pending_create(browsing, process_browsing, req,
+			browsing_req_destroy);
+
+	req->p = p;
+
+	g_queue_push_tail(browsing->queue, p);
+
+	if (browsing->process_id == 0)
+		browsing->process_id = g_idle_add(process_queue, browsing);
+
+	return 0;
+}
+
 static char *op2str(uint8_t op)
 {
 	switch (op & 0x7f) {
diff --git a/profiles/audio/avctp.h b/profiles/audio/avctp.h
index c25a3b3..b9107bd 100644
--- a/profiles/audio/avctp.h
+++ b/profiles/audio/avctp.h
@@ -82,6 +82,9 @@ typedef size_t (*avctp_control_pdu_cb) (struct avctp *session,
 typedef gboolean (*avctp_rsp_cb) (struct avctp *session, uint8_t code,
 					uint8_t subunit, uint8_t *operands,
 					size_t operand_count, void *user_data);
+typedef gboolean (*avctp_browsing_rsp_cb) (struct avctp *session,
+					uint8_t *operands, size_t operand_count,
+					void *user_data);
 typedef size_t (*avctp_browsing_pdu_cb) (struct avctp *session,
 					uint8_t transaction,
 					uint8_t *operands, size_t operand_count,
@@ -116,3 +119,6 @@ int avctp_send_vendordep_req(struct avctp *session, uint8_t code,
 					uint8_t subunit, uint8_t *operands,
 					size_t operand_count,
 					avctp_rsp_cb func, void *user_data);
+int avctp_send_browsing_req(struct avctp *session,
+				uint8_t *operands, size_t operand_count,
+				avctp_browsing_rsp_cb func, void *user_data);
-- 
1.8.1

--
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