diff --git a/audio/avctp.c b/audio/avctp.c
index aa9a1ca..228d5b7 100644
--- a/audio/avctp.c
+++ b/audio/avctp.c
uint16_t imtu;
uint16_t omtu;
uint8_t *buffer;
+ GSList *handlers;
};
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;
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);
g_source_remove(chan->watch);
g_free(chan->buffer);
+ g_slist_free_full(chan->handlers, g_free);
g_free(chan);
}
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;
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)
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);
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);
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;
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;
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;
}
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)
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;
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;
+ }
}
}
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
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 295a1e1..ae2f959 100644
--- a/audio/avrcp.c
+++ b/audio/avrcp.c
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];
{ },
};
+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)
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);
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)
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(
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);
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
size_t avrcp_handle_vendor_reject(uint8_t *code, uint8_t *operands);
+size_t avrcp_browsing_general_reject(uint8_t *operands);