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
*/
#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 */
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;
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;
uint16_t omtu;
uint8_t *buffer;
GSList *handlers;
- struct avctp_pending_req *p;
- GQueue *queue;
+ GSList *queues;
GSList *processed;
- guint process_id;
GDestroyNotify destroy;
};
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);
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);
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];
{
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;
{
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;
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");
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;
}
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;
if (p == NULL)
return FALSE;
- chan->p = p;
+ queue->p = p;
return FALSE;
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)
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:
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);
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) {
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;
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;
}
{
struct avctp *session = data;
struct avctp_channel *browsing = session->browsing;
+ struct avctp_queue *queue;
char address[18];
uint16_t imtu, omtu;
GError *gerr = NULL;
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;
}
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;
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;
}
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);
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)
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;
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;
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;
}
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;
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;
}
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);
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);