From 1a291c3482e7fdb498c178650d318b991b27a3d2 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 22 Oct 2007 14:11:04 +0000 Subject: Add skeleton for AVRCP support --- audio/control.c | 586 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 586 insertions(+) create mode 100644 audio/control.c (limited to 'audio/control.c') diff --git a/audio/control.c b/audio/control.c new file mode 100644 index 00000000..4ffaa77e --- /dev/null +++ b/audio/control.c @@ -0,0 +1,586 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2007 Marcel Holtmann + * Copyright (C) 2007 Nokia Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include "logging.h" +#include "device.h" +#include "manager.h" +#include "avdtp.h" +#include "control.h" + +#define AVCTP_PSM 23 + +static DBusConnection *connection = NULL; + +static uint32_t tg_record_id = 0; +static uint32_t ct_record_id = 0; + +static GIOChannel *avctp_server = NULL; + +static GSList *sessions = NULL; + +#if __BYTE_ORDER == __LITTLE_ENDIAN + +struct avctp_header { + uint8_t ipid:1; + uint8_t cr:1; + uint8_t packet_type:2; + uint8_t transaction:4; + uint16_t pid; +} __attribute__ ((packed)); + +struct avrcp_header { + uint8_t code:4; + uint8_t _hdr0:4; + uint8_t subunit_id:3; + uint8_t subunit_type:5; + uint8_t opcode; +} __attribute__ ((packed)); + +#elif __BYTE_ORDER == __BIG_ENDIAN + +struct avctp_header { + uint8_t transaction:4; + uint8_t packet_type:2; + uint8_t cr:1; + uint8_t ipid:1; + uint16_t pid; +} __attribute__ ((packed)); + +struct avrcp_header { + uint8_t _hdr0:4; + uint8_t code:4; + uint8_t subunit_type:5; + uint8_t subunit_id:3; + uint8_t opcode; +} __attribute__ ((packed)); + +#else +#error "Unknown byte order" +#endif + +struct avctp { + bdaddr_t src; + bdaddr_t dst; + + int sock; + + guint io; + + uint16_t mtu; + + DBusPendingCall *pending_auth; +}; + +static int avrcp_ct_record(sdp_buf_t *buf) +{ + sdp_list_t *svclass_id, *pfseq, *apseq, *root; + uuid_t root_uuid, l2cap, avctp, avrct; + sdp_profile_desc_t profile[1]; + sdp_list_t *aproto, *proto[2]; + sdp_record_t record; + sdp_data_t *psm, *version, *features; + uint16_t lp = AVCTP_PSM, ver = 0x0103, feat = 0x000f; + int ret = 0; + + memset(&record, 0, sizeof(sdp_record_t)); + + sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP); + root = sdp_list_append(0, &root_uuid); + sdp_set_browse_groups(&record, root); + + /* Service Class ID List */ + sdp_uuid16_create(&avrct, AV_REMOTE_SVCLASS_ID); + svclass_id = sdp_list_append(0, &avrct); + sdp_set_service_classes(&record, svclass_id); + + /* Protocol Descriptor List */ + sdp_uuid16_create(&l2cap, L2CAP_UUID); + proto[0] = sdp_list_append(0, &l2cap); + psm = sdp_data_alloc(SDP_UINT16, &lp); + proto[0] = sdp_list_append(proto[0], psm); + apseq = sdp_list_append(0, proto[0]); + + sdp_uuid16_create(&avctp, AVCTP_UUID); + proto[1] = sdp_list_append(0, &avctp); + version = sdp_data_alloc(SDP_UINT16, &ver); + proto[1] = sdp_list_append(proto[1], version); + apseq = sdp_list_append(apseq, proto[1]); + + aproto = sdp_list_append(0, apseq); + sdp_set_access_protos(&record, aproto); + + /* Bluetooth Profile Descriptor List */ + sdp_uuid16_create(&profile[0].uuid, AV_REMOTE_PROFILE_ID); + profile[0].version = ver; + pfseq = sdp_list_append(0, &profile[0]); + sdp_set_profile_descs(&record, pfseq); + + features = sdp_data_alloc(SDP_UINT16, &feat); + sdp_attr_add(&record, SDP_ATTR_SUPPORTED_FEATURES, features); + + sdp_set_info_attr(&record, "AVRCP CT", 0, 0); + + if (sdp_gen_record_pdu(&record, buf) < 0) + ret = -1; + else + ret = 0; + + free(psm); + free(version); + sdp_list_free(proto[0], 0); + sdp_list_free(proto[1], 0); + sdp_list_free(apseq, 0); + sdp_list_free(pfseq, 0); + sdp_list_free(aproto, 0); + sdp_list_free(root, 0); + sdp_list_free(svclass_id, 0); + sdp_list_free(record.attrlist, (sdp_free_func_t) sdp_data_free); + sdp_list_free(record.pattern, free); + + return ret; +} + +static int avrcp_tg_record(sdp_buf_t *buf) +{ + sdp_list_t *svclass_id, *pfseq, *apseq, *root; + uuid_t root_uuid, l2cap, avctp, avrtg; + sdp_profile_desc_t profile[1]; + sdp_list_t *aproto, *proto[2]; + sdp_record_t record; + sdp_data_t *psm, *version, *features; + uint16_t lp = AVCTP_PSM, ver = 0x0103, feat = 0x000f; + int ret = 0; + + memset(&record, 0, sizeof(sdp_record_t)); + + sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP); + root = sdp_list_append(0, &root_uuid); + sdp_set_browse_groups(&record, root); + + /* Service Class ID List */ + sdp_uuid16_create(&avrtg, AV_REMOTE_TARGET_SVCLASS_ID); + svclass_id = sdp_list_append(0, &avrtg); + sdp_set_service_classes(&record, svclass_id); + + /* Protocol Descriptor List */ + sdp_uuid16_create(&l2cap, L2CAP_UUID); + proto[0] = sdp_list_append(0, &l2cap); + psm = sdp_data_alloc(SDP_UINT16, &lp); + proto[0] = sdp_list_append(proto[0], psm); + apseq = sdp_list_append(0, proto[0]); + + sdp_uuid16_create(&avctp, AVCTP_UUID); + proto[1] = sdp_list_append(0, &avctp); + version = sdp_data_alloc(SDP_UINT16, &ver); + proto[1] = sdp_list_append(proto[1], version); + apseq = sdp_list_append(apseq, proto[1]); + + aproto = sdp_list_append(0, apseq); + sdp_set_access_protos(&record, aproto); + + /* Bluetooth Profile Descriptor List */ + sdp_uuid16_create(&profile[0].uuid, AV_REMOTE_PROFILE_ID); + profile[0].version = ver; + pfseq = sdp_list_append(0, &profile[0]); + sdp_set_profile_descs(&record, pfseq); + + features = sdp_data_alloc(SDP_UINT16, &feat); + sdp_attr_add(&record, SDP_ATTR_SUPPORTED_FEATURES, features); + + sdp_set_info_attr(&record, "AVRCP TG", 0, 0); + + if (sdp_gen_record_pdu(&record, buf) < 0) + ret = -1; + else + ret = 0; + + free(psm); + free(version); + sdp_list_free(proto[0], 0); + sdp_list_free(proto[1], 0); + sdp_list_free(apseq, 0); + sdp_list_free(aproto, 0); + sdp_list_free(pfseq, 0); + sdp_list_free(root, 0); + sdp_list_free(svclass_id, 0); + sdp_list_free(record.attrlist, (sdp_free_func_t) sdp_data_free); + sdp_list_free(record.pattern, free); + + return ret; +} + +static GIOChannel *avctp_server_socket(void) +{ + int sock, lm; + struct sockaddr_l2 addr; + GIOChannel *io; + + sock = socket(AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP); + if (sock < 0) { + error("AVCTP server socket: %s (%d)", strerror(errno), errno); + return NULL; + } + + lm = L2CAP_LM_SECURE; + if (setsockopt(sock, SOL_L2CAP, L2CAP_LM, &lm, sizeof(lm)) < 0) { + error("AVCTP server setsockopt: %s (%d)", strerror(errno), errno); + close(sock); + return NULL; + } + + memset(&addr, 0, sizeof(addr)); + addr.l2_family = AF_BLUETOOTH; + bacpy(&addr.l2_bdaddr, BDADDR_ANY); + addr.l2_psm = htobs(AVCTP_PSM); + + if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + error("AVCTP server bind: %s", strerror(errno), errno); + close(sock); + return NULL; + } + + if (listen(sock, 4) < 0) { + error("AVCTP server listen: %s", strerror(errno), errno); + close(sock); + return NULL; + } + + io = g_io_channel_unix_new(sock); + if (!io) { + error("Unable to allocate new io channel"); + close(sock); + return NULL; + } + + return io; +} + +static struct avctp *find_session(bdaddr_t *src, bdaddr_t *dst) +{ + GSList *l; + + for (l = sessions; l != NULL; l = g_slist_next(l)) { + struct avctp *s = l->data; + + if (bacmp(src, &s->src) || bacmp(dst, &s->dst)) + continue; + + return s; + } + + return NULL; +} + +static void avctp_unref(struct avctp *session) +{ + sessions = g_slist_remove(sessions, session); + if (session->sock >= 0) + close(session->sock); + if (session->io) + g_source_remove(session->io); + g_free(session); +} + +static struct avctp *avctp_get(bdaddr_t *src, bdaddr_t *dst) +{ + struct avctp *session; + + assert(src != NULL); + assert(dst != NULL); + + session = find_session(src, dst); + if (session) { + if (session->pending_auth) + return NULL; + else + return session; + } + + session = g_new0(struct avctp, 1); + + session->sock = -1; + bacpy(&session->src, src); + bacpy(&session->dst, dst); + + sessions = g_slist_append(sessions, session); + + return session; +} + +static gboolean session_cb(GIOChannel *chan, GIOCondition cond, + gpointer data) +{ + struct avctp *session = data; + char buf[1024]; + struct avctp_header *avctp; + struct avrcp_header *avrcp; + int ret; + + if (!(cond | G_IO_IN)) + goto failed; + + ret = read(session->sock, buf, sizeof(buf)); + if (ret <= 0) + goto failed; + + debug("Got %d bytes of data for AVCTP session %p", ret, session); + + if (ret < sizeof(struct avctp_header)) { + error("Too small AVCTP packet"); + goto failed; + } + + avctp = (struct avctp_header *) buf; + + debug("AVCTP transaction %u, packet type %u, C/R %u, IPID %u, " + "PID 0x%04X", + avctp->transaction, avctp->packet_type, + avctp->cr, avctp->ipid, ntohs(avctp->pid)); + + ret -= sizeof(struct avctp_header); + if (ret < sizeof(struct avrcp_header)) { + error("Too small AVRCP packet"); + goto failed; + } + + avrcp = (struct avrcp_header *) (buf + sizeof(struct avctp_header)); + + debug("AVRCP %s 0x%01X, subunit_type 0x%02X, subunit_id 0x%01X, " + "opcode 0x%02X", avctp->cr ? "response" : "command", + avrcp->code, avrcp->subunit_type, avrcp->subunit_id, + avrcp->opcode); + + return TRUE; + +failed: + debug("AVCTP session %p got disconnected", session); + avctp_unref(session); + return FALSE; +} + +static void auth_cb(DBusPendingCall *call, void *data) +{ + GIOChannel *io; + struct avctp *session = data; + DBusMessage *reply = dbus_pending_call_steal_reply(call); + DBusError err; + + dbus_pending_call_unref(session->pending_auth); + session->pending_auth = NULL; + + dbus_error_init(&err); + if (dbus_set_error_from_message(&err, reply)) { + error("Access denied: %s", err.message); + + if (dbus_error_has_name(&err, DBUS_ERROR_NO_REPLY)) { + debug("Canceling authorization request"); + manager_cancel_authorize(&session->dst, + ADVANCED_AUDIO_UUID, + NULL); + } + + avctp_unref(session); + + dbus_message_unref(reply); + + return; + } + + g_source_remove(session->io); + + io = g_io_channel_unix_new(session->sock); + session->io = g_io_add_watch(io, + G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL, + (GIOFunc) session_cb, session); + g_io_channel_unref(io); +} + +static gboolean avctp_server_cb(GIOChannel *chan, GIOCondition cond, void *data) +{ + int srv_sk, cli_sk; + socklen_t size; + struct sockaddr_l2 addr; + struct l2cap_options l2o; + bdaddr_t src, dst; + struct avctp *session; + GIOChannel *io; + GIOCondition flags = G_IO_ERR | G_IO_HUP | G_IO_NVAL; + char address[18]; + + if (cond & G_IO_NVAL) + return FALSE; + + if (cond & (G_IO_HUP | G_IO_ERR)) { + error("Hangup or error on AVCTP server socket"); + g_io_channel_close(chan); + raise(SIGTERM); + return FALSE; + } + + srv_sk = g_io_channel_unix_get_fd(chan); + + size = sizeof(struct sockaddr_l2); + cli_sk = accept(srv_sk, (struct sockaddr *) &addr, &size); + if (cli_sk < 0) { + error("AVCTP accept: %s (%d)", strerror(errno), errno); + return TRUE; + } + + bacpy(&dst, &addr.l2_bdaddr); + + ba2str(&dst, address); + debug("AVCTP: incoming connect from %s", address); + + size = sizeof(struct sockaddr_l2); + if (getsockname(cli_sk, (struct sockaddr *) &addr, &size) < 0) { + error("getsockname: %s (%d)", strerror(errno), errno); + close(cli_sk); + return TRUE; + } + + bacpy(&src, &addr.l2_bdaddr); + + memset(&l2o, 0, sizeof(l2o)); + size = sizeof(l2o); + if (getsockopt(cli_sk, SOL_L2CAP, L2CAP_OPTIONS, &l2o, &size) < 0) { + error("getsockopt(L2CAP_OPTIONS): %s (%d)", strerror(errno), + errno); + close(cli_sk); + return TRUE; + } + + session = avctp_get(&src, &dst); + + if (session->sock >= 0) { + error("Refusing unexpected connect from %s", address); + close(cli_sk); + return TRUE; + } + + if (avdtp_is_connected(&src, &dst)) + goto proceed; + + if (!manager_authorize(&dst, AVRCP_TARGET_UUID, auth_cb, session, + &session->pending_auth)) { + close(cli_sk); + avctp_unref(session); + return TRUE; + } + +proceed: + session->mtu = l2o.imtu; + session->sock = cli_sk; + + io = g_io_channel_unix_new(session->sock); + if (!session->pending_auth) + flags |= G_IO_IN; + session->io = g_io_add_watch(io, flags, (GIOFunc) session_cb, session); + g_io_channel_unref(io); + + return TRUE; +} + +int control_init(DBusConnection *conn) +{ + sdp_buf_t buf; + + if (avctp_server) + return 0; + + connection = dbus_connection_ref(conn); + + if (avrcp_tg_record(&buf) < 0) { + error("Unable to allocate new service record"); + return -1; + } + + tg_record_id = add_service_record(conn, &buf); + free(buf.data); + + if (!tg_record_id) { + error("Unable to register AVRCP target service record"); + return -1; + } + + if (avrcp_ct_record(&buf) < 0) { + error("Unable to allocate new service record"); + return -1; + } + + ct_record_id = add_service_record(conn, &buf); + free(buf.data); + + if (!ct_record_id) { + error("Unable to register AVRCP controller service record"); + return -1; + } + + avctp_server = avctp_server_socket(); + if (!avctp_server) + return -1; + + g_io_add_watch(avctp_server, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, + (GIOFunc) avctp_server_cb, NULL); + + return 0; +} + +void control_exit(void) +{ + if (!avctp_server) + return; + + g_io_channel_close(avctp_server); + g_io_channel_unref(avctp_server); + avctp_server = NULL; + + remove_service_record(connection, ct_record_id); + ct_record_id = 0; + + remove_service_record(connection, ct_record_id); + ct_record_id = 0; + + dbus_connection_unref(connection); + connection = NULL; +} + -- cgit From 0a6d5bb52e986e7a30432ed8495649f55afa6c01 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 22 Oct 2007 14:13:37 +0000 Subject: Fix copyright line formating --- audio/control.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'audio/control.c') diff --git a/audio/control.c b/audio/control.c index 4ffaa77e..50712137 100644 --- a/audio/control.c +++ b/audio/control.c @@ -2,8 +2,8 @@ * * BlueZ - Bluetooth protocol stack for Linux * - * Copyright (C) 2007 Marcel Holtmann - * Copyright (C) 2007 Nokia Corporation + * Copyright (C) 2007 Marcel Holtmann + * Copyright (C) 2007 Nokia Corporation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by -- cgit From 0a428df5dffc20af827516422589465e654fd044 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 22 Oct 2007 14:51:53 +0000 Subject: Fix UUID in CancelAuthorize call --- audio/control.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'audio/control.c') diff --git a/audio/control.c b/audio/control.c index 50712137..ae853fe8 100644 --- a/audio/control.c +++ b/audio/control.c @@ -415,7 +415,7 @@ static void auth_cb(DBusPendingCall *call, void *data) if (dbus_error_has_name(&err, DBUS_ERROR_NO_REPLY)) { debug("Canceling authorization request"); manager_cancel_authorize(&session->dst, - ADVANCED_AUDIO_UUID, + AVRCP_TARGET_UUID, NULL); } -- cgit From 723ff52c61b23cfaac0293b9d5a206f8df94396e Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 22 Oct 2007 14:59:22 +0000 Subject: Show number of operands in packet --- audio/control.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'audio/control.c') diff --git a/audio/control.c b/audio/control.c index ae853fe8..c61a79dd 100644 --- a/audio/control.c +++ b/audio/control.c @@ -386,9 +386,10 @@ static gboolean session_cb(GIOChannel *chan, GIOCondition cond, avrcp = (struct avrcp_header *) (buf + sizeof(struct avctp_header)); debug("AVRCP %s 0x%01X, subunit_type 0x%02X, subunit_id 0x%01X, " - "opcode 0x%02X", avctp->cr ? "response" : "command", + "opcode 0x%02X, %d operands", + avctp->cr ? "response" : "command", avrcp->code, avrcp->subunit_type, avrcp->subunit_id, - avrcp->opcode); + avrcp->opcode, ret - sizeof(struct avctp_header)); return TRUE; -- cgit From 0ad3f2251089e00a57b6aa6def396e24f30ab1e4 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 23 Oct 2007 07:54:36 +0000 Subject: Integrate AVRCP further with the rest of the audio service --- audio/control.c | 277 +++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 274 insertions(+), 3 deletions(-) (limited to 'audio/control.c') diff --git a/audio/control.c b/audio/control.c index c61a79dd..83f48df4 100644 --- a/audio/control.c +++ b/audio/control.c @@ -41,6 +41,8 @@ #include #include +#include "dbus.h" +#include "dbus-helper.h" #include "logging.h" #include "device.h" #include "manager.h" @@ -58,6 +60,12 @@ static GIOChannel *avctp_server = NULL; static GSList *sessions = NULL; +typedef enum { + AVCTP_STATE_DISCONNECTED = 0, + AVCTP_STATE_CONNECTING, + AVCTP_STATE_CONNECTED +} avctp_state_t; + #if __BYTE_ORDER == __LITTLE_ENDIAN struct avctp_header { @@ -99,6 +107,10 @@ struct avrcp_header { #endif struct avctp { + struct device *dev; + + avctp_state_t state; + bdaddr_t src; bdaddr_t dst; @@ -111,6 +123,11 @@ struct avctp { DBusPendingCall *pending_auth; }; +struct control { + struct avctp *session; +}; + + static int avrcp_ct_record(sdp_buf_t *buf) { sdp_list_t *svclass_id, *pfseq, *apseq, *root; @@ -314,10 +331,25 @@ static struct avctp *find_session(bdaddr_t *src, bdaddr_t *dst) static void avctp_unref(struct avctp *session) { sessions = g_slist_remove(sessions, session); + + if (session->pending_auth) { + manager_cancel_authorize(&session->dst, AVRCP_TARGET_UUID, + NULL); + dbus_pending_call_cancel(session->pending_auth); + dbus_pending_call_unref(session->pending_auth); + } + + if (session->state == AVCTP_STATE_CONNECTED) + dbus_connection_emit_signal(session->dev->conn, + session->dev->path, + AUDIO_CONTROL_INTERFACE, + "Disconnected", + DBUS_TYPE_INVALID); if (session->sock >= 0) close(session->sock); if (session->io) g_source_remove(session->io); + session->dev->control->session = NULL; g_free(session); } @@ -427,6 +459,16 @@ static void auth_cb(DBusPendingCall *call, void *data) return; } + session->state = AVCTP_STATE_CONNECTED; + + session->dev = manager_device_connected(&session->dst, + AVRCP_TARGET_UUID); + session->dev->control->session = session; + + dbus_connection_emit_signal(session->dev->conn, session->dev->path, + AUDIO_CONTROL_INTERFACE, "Connected", + DBUS_TYPE_INVALID); + g_source_remove(session->io); io = g_io_channel_unix_new(session->sock); @@ -498,6 +540,8 @@ static gboolean avctp_server_cb(GIOChannel *chan, GIOCondition cond, void *data) return TRUE; } + session->state = AVCTP_STATE_CONNECTING; + if (avdtp_is_connected(&src, &dst)) goto proceed; @@ -513,15 +557,176 @@ proceed: session->sock = cli_sk; io = g_io_channel_unix_new(session->sock); - if (!session->pending_auth) + if (!session->pending_auth) { + session->state = AVCTP_STATE_CONNECTED; + session->dev = manager_device_connected(&dst, + AVRCP_TARGET_UUID); + session->dev->control->session = session; flags |= G_IO_IN; + dbus_connection_emit_signal(session->dev->conn, + session->dev->path, + AUDIO_CONTROL_INTERFACE, + "Connected", + DBUS_TYPE_INVALID); + } + session->io = g_io_add_watch(io, flags, (GIOFunc) session_cb, session); g_io_channel_unref(io); return TRUE; } -int control_init(DBusConnection *conn) +static gboolean avctp_connect_cb(GIOChannel *chan, GIOCondition cond, + gpointer data) +{ + struct avctp *session = data; + struct l2cap_options l2o; + socklen_t len; + int ret, err, sk; + char address[18]; + + if (cond & G_IO_NVAL) + return FALSE; + + sk = g_io_channel_unix_get_fd(chan); + + ba2str(&session->dst, address); + + len = sizeof(ret); + if (getsockopt(sk, SOL_SOCKET, SO_ERROR, &ret, &len) < 0) { + err = errno; + error("getsockopt(SO_ERROR): %s (%d)", strerror(err), err); + goto failed; + } + + if (ret != 0) { + err = ret; + error("AVCTP connect(%s): %s (%d)", address, strerror(err), + err); + goto failed; + } + + if (cond & G_IO_HUP) { + err = EIO; + goto failed; + } + + debug("AVCTP: connected to %s", address); + + memset(&l2o, 0, sizeof(l2o)); + len = sizeof(l2o); + if (getsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &l2o, + &len) < 0) { + err = errno; + error("getsockopt(L2CAP_OPTIONS): %s (%d)", strerror(err), + err); + goto failed; + } + + dbus_connection_emit_signal(session->dev->conn, session->dev->path, + AUDIO_CONTROL_INTERFACE, "Connected", + DBUS_TYPE_INVALID); + + session->state = AVCTP_STATE_CONNECTED; + + session->mtu = l2o.imtu; + session->io = g_io_add_watch(chan, + G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL, + (GIOFunc) session_cb, session); + return FALSE; + +failed: + close(sk); + + avctp_unref(session); + + return FALSE; +} + +gboolean avrcp_connect(struct device *dev) +{ + struct control *control = dev->control; + struct avctp *session; + struct sockaddr_l2 l2a; + GIOChannel *io; + int sk; + + if (control->session) + return TRUE; + + session = avctp_get(&dev->src, &dev->dst); + session->dev = dev; + + memset(&l2a, 0, sizeof(l2a)); + l2a.l2_family = AF_BLUETOOTH; + bacpy(&l2a.l2_bdaddr, &dev->src); + + sk = socket(AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP); + if (sk < 0) { + error("Cannot create L2CAP socket. %s(%d)", strerror(errno), + errno); + goto failed; + } + + if (bind(sk, (struct sockaddr *) &l2a, sizeof(l2a)) < 0) { + error("Bind failed. %s (%d)", strerror(errno), errno); + goto failed; + } + + memset(&l2a, 0, sizeof(l2a)); + l2a.l2_family = AF_BLUETOOTH; + bacpy(&l2a.l2_bdaddr, &dev->dst); + l2a.l2_psm = htobs(AVCTP_PSM); + + if (set_nonblocking(sk) < 0) { + error("Set non blocking: %s (%d)", strerror(errno), errno); + goto failed; + } + + io = g_io_channel_unix_new(sk); + g_io_channel_set_close_on_unref(io, FALSE); + session->sock = sk; + + if (connect(sk, (struct sockaddr *) &l2a, sizeof(l2a)) < 0) { + if (!(errno == EAGAIN || errno == EINPROGRESS)) { + error("Connect failed. %s(%d)", strerror(errno), + errno); + g_io_channel_close(io); + g_io_channel_unref(io); + goto failed; + } + + session->state = AVCTP_STATE_CONNECTING; + + g_io_add_watch(io, G_IO_OUT | G_IO_HUP | G_IO_ERR | G_IO_NVAL, + (GIOFunc) avctp_connect_cb, session); + } else + avctp_connect_cb(io, G_IO_OUT, session); + + g_io_channel_unref(io); + + control->session = session; + + return TRUE; + +failed: + avctp_unref(session); + return FALSE; +} + +void avrcp_disconnect(struct device *dev) +{ + struct control *control = dev->control; + struct avctp *session = control->session; + + if (!session) + return; + + avctp_unref(session); + control->session = NULL; +} + +int avrcp_init(DBusConnection *conn) { sdp_buf_t buf; @@ -566,7 +771,7 @@ int control_init(DBusConnection *conn) return 0; } -void control_exit(void) +void avrcp_exit(void) { if (!avctp_server) return; @@ -585,3 +790,69 @@ void control_exit(void) connection = NULL; } +static DBusHandlerResult control_is_connected(DBusConnection *conn, + DBusMessage *msg, + void *data) +{ + struct device *device = data; + struct control *control = device->control; + DBusMessage *reply; + dbus_bool_t connected; + + reply = dbus_message_new_method_return(msg); + if (!reply) + return DBUS_HANDLER_RESULT_NEED_MEMORY; + + connected = (control->session != NULL); + + dbus_message_append_args(reply, DBUS_TYPE_BOOLEAN, &connected, + DBUS_TYPE_INVALID); + + send_message_and_unref(conn, reply); + + return DBUS_HANDLER_RESULT_HANDLED; +} + +static DBusMethodVTable control_methods[] = { + { "IsConnected", control_is_connected, "", "b" }, + { NULL, NULL, NULL, NULL } +}; + +static DBusSignalVTable control_signals[] = { + { "Connected", "" }, + { "Disconnected", "" }, + { NULL, NULL } +}; + +struct control *control_init(struct device *dev) +{ + if (!dbus_connection_register_interface(dev->conn, dev->path, + AUDIO_CONTROL_INTERFACE, + control_methods, + control_signals, NULL)) + return NULL; + + return g_new0(struct control, 1); +} + +void control_free(struct device *dev) +{ + struct control *control = dev->control; + + if (control->session) + avctp_unref(control->session); + + g_free(control); + dev->control = NULL; +} + +gboolean control_is_active(struct device *dev) +{ + struct control *control = dev->control; + + if (control->session && + control->session->state != AVCTP_STATE_DISCONNECTED) + return TRUE; + + return FALSE; +} -- cgit From 4b45ea5d70f9699fd435120ff860373630c87a42 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 23 Oct 2007 08:56:06 +0000 Subject: Add basic panel passthrough command parsing --- audio/control.c | 87 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 84 insertions(+), 3 deletions(-) (limited to 'audio/control.c') diff --git a/audio/control.c b/audio/control.c index 83f48df4..c1e59be9 100644 --- a/audio/control.c +++ b/audio/control.c @@ -51,6 +51,41 @@ #define AVCTP_PSM 23 +/* Message types */ +#define AVCTP_COMMAND 0 +#define AVCTP_RESPONSE 1 + +/* Packet types */ +#define AVCTP_PACKET_SINGLE 0 +#define AVCTP_PACKET_START 1 +#define AVCTP_PACKET_CONTINUE 2 +#define AVCTP_PACKET_END 3 + +/* ctype entries */ +#define CTYPE_CONTROL 0x0 +#define CTYPE_STATUS 0x1 +#define CTYPE_ACCEPTED 0x9 +#define CTYPE_STABLE 0xC + +/* opcodes */ +#define OP_UNITINFO 0x30 +#define OP_SUBUNITINFO 0x31 +#define OP_PASSTHROUGH 0x7c + +/* subunits of interest */ +#define SUBUNIT_PANEL 0x09 + +/* operands in passthrough commands */ +#define VOLUP_OP 0x41 +#define VOLDOWN_OP 0x42 +#define MUTE_OP 0x43 + +#define PLAY_OP 0x44 +#define STOP_OP 0x45 +#define PAUSE_OP 0x46 +#define NEXT_OP 0x4b +#define PREV_OP 0x4c + static DBusConnection *connection = NULL; static uint32_t tg_record_id = 0; @@ -379,14 +414,41 @@ static struct avctp *avctp_get(bdaddr_t *src, bdaddr_t *dst) return session; } +static void handle_panel_passthrough(const unsigned char *operands, int operand_count) +{ + if (operand_count == 0) + return; + + switch (operands[0]) { + case PLAY_OP: + debug("AVRCP: got PLAY"); + break; + case STOP_OP: + debug("AVRCP: got STOP"); + break; + case PAUSE_OP: + debug("AVRCP: got PAUSE"); + break; + case NEXT_OP: + debug("AVRCP: got NEXT"); + break; + case PREV_OP: + debug("AVRCP: got PREV"); + break; + default: + debug("AVRCP: got unknown operand 0x%02X", operands[0]); + break; + } +} + static gboolean session_cb(GIOChannel *chan, GIOCondition cond, gpointer data) { struct avctp *session = data; - char buf[1024]; + unsigned char buf[1024], *operands; struct avctp_header *avctp; struct avrcp_header *avrcp; - int ret; + int ret, packet_size, operand_count; if (!(cond | G_IO_IN)) goto failed; @@ -402,6 +464,8 @@ static gboolean session_cb(GIOChannel *chan, GIOCondition cond, goto failed; } + packet_size = ret; + avctp = (struct avctp_header *) buf; debug("AVCTP transaction %u, packet type %u, C/R %u, IPID %u, " @@ -417,11 +481,28 @@ static gboolean session_cb(GIOChannel *chan, GIOCondition cond, avrcp = (struct avrcp_header *) (buf + sizeof(struct avctp_header)); + ret -= sizeof(struct avrcp_header); + + operands = buf + sizeof(struct avctp_header) + sizeof(struct avrcp_header); + operand_count = ret; + debug("AVRCP %s 0x%01X, subunit_type 0x%02X, subunit_id 0x%01X, " "opcode 0x%02X, %d operands", avctp->cr ? "response" : "command", avrcp->code, avrcp->subunit_type, avrcp->subunit_id, - avrcp->opcode, ret - sizeof(struct avctp_header)); + avrcp->opcode, operand_count); + + if (avctp->packet_type == AVCTP_PACKET_SINGLE && + avctp->cr == AVCTP_COMMAND && + avctp->pid == htons(AV_REMOTE_SVCLASS_ID) && + avrcp->code == CTYPE_CONTROL && + avrcp->subunit_type == SUBUNIT_PANEL && + avrcp->opcode == OP_PASSTHROUGH) { + handle_panel_passthrough(operands, operand_count); + avctp->cr = AVCTP_RESPONSE; + avrcp->code = CTYPE_ACCEPTED; + ret = write(session->sock, buf, packet_size); + } return TRUE; -- cgit From de72271829f6bfd21aa6550a2ac6d81e35b53cad Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Tue, 23 Oct 2007 17:17:47 +0000 Subject: Update copyright information --- audio/control.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'audio/control.c') diff --git a/audio/control.c b/audio/control.c index c1e59be9..5048bb17 100644 --- a/audio/control.c +++ b/audio/control.c @@ -2,8 +2,9 @@ * * BlueZ - Bluetooth protocol stack for Linux * - * Copyright (C) 2007 Marcel Holtmann - * Copyright (C) 2007 Nokia Corporation + * Copyright (C) 2006-2007 Nokia Corporation + * Copyright (C) 2004-2007 Marcel Holtmann + * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by -- cgit From f6e4c2b47dbf0761224232888dca66ed0bddd1c7 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 24 Oct 2007 17:25:56 +0000 Subject: Improve AVRCP button press decoding --- audio/control.c | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) (limited to 'audio/control.c') diff --git a/audio/control.c b/audio/control.c index 5048bb17..c69c610f 100644 --- a/audio/control.c +++ b/audio/control.c @@ -57,10 +57,10 @@ #define AVCTP_RESPONSE 1 /* Packet types */ -#define AVCTP_PACKET_SINGLE 0 -#define AVCTP_PACKET_START 1 -#define AVCTP_PACKET_CONTINUE 2 -#define AVCTP_PACKET_END 3 +#define AVCTP_PACKET_SINGLE 0 +#define AVCTP_PACKET_START 1 +#define AVCTP_PACKET_CONTINUE 2 +#define AVCTP_PACKET_END 3 /* ctype entries */ #define CTYPE_CONTROL 0x0 @@ -417,27 +417,34 @@ static struct avctp *avctp_get(bdaddr_t *src, bdaddr_t *dst) static void handle_panel_passthrough(const unsigned char *operands, int operand_count) { + const char *status; + if (operand_count == 0) return; - switch (operands[0]) { + if (operands[0] & 0x80) + status = "released"; + else + status = "pressed"; + + switch (operands[0] & 0x7F) { case PLAY_OP: - debug("AVRCP: got PLAY"); + debug("AVRCP: PLAY %s", status); break; case STOP_OP: - debug("AVRCP: got STOP"); + debug("AVRCP: STOP %s", status); break; case PAUSE_OP: - debug("AVRCP: got PAUSE"); + debug("AVRCP: PAUSE %s", status); break; case NEXT_OP: - debug("AVRCP: got NEXT"); + debug("AVRCP: NEXT %s", status); break; case PREV_OP: - debug("AVRCP: got PREV"); + debug("AVRCP: PREV %s", status); break; default: - debug("AVRCP: got unknown operand 0x%02X", operands[0]); + debug("AVRCP: unknown button 0x%02X %s", operands[0] & 0x7F, status); break; } } -- cgit From 589d60855eedc4d207ed6e79cae3e18c48a4586f Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 25 Oct 2007 10:38:47 +0000 Subject: Add uinput support for AVRCP --- audio/control.c | 155 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 150 insertions(+), 5 deletions(-) (limited to 'audio/control.c') diff --git a/audio/control.c b/audio/control.c index c69c610f..bd100331 100644 --- a/audio/control.c +++ b/audio/control.c @@ -32,6 +32,9 @@ #include #include #include +#include +#include +#include #include #include @@ -45,6 +48,7 @@ #include "dbus.h" #include "dbus-helper.h" #include "logging.h" +#include "uinput.h" #include "device.h" #include "manager.h" #include "avdtp.h" @@ -84,6 +88,8 @@ #define PLAY_OP 0x44 #define STOP_OP 0x45 #define PAUSE_OP 0x46 +#define REWIND_OP 0x48 +#define FAST_FORWARD_OP 0x49 #define NEXT_OP 0x4b #define PREV_OP 0x4c @@ -150,6 +156,8 @@ struct avctp { bdaddr_t src; bdaddr_t dst; + int uinput; + int sock; guint io; @@ -163,7 +171,6 @@ struct control { struct avctp *session; }; - static int avrcp_ct_record(sdp_buf_t *buf) { sdp_list_t *svclass_id, *pfseq, *apseq, *root; @@ -385,10 +392,77 @@ static void avctp_unref(struct avctp *session) close(session->sock); if (session->io) g_source_remove(session->io); + session->dev->control->session = NULL; + + if (session->uinput >= 0) { + ioctl(session->uinput, UI_DEV_DESTROY); + close(session->uinput); + } + g_free(session); } +static int uinput_create(char *name) +{ + struct uinput_dev dev; + int fd, err; + + fd = open("/dev/uinput", O_RDWR); + if (fd < 0) { + fd = open("/dev/input/uinput", O_RDWR); + if (fd < 0) { + fd = open("/dev/misc/uinput", O_RDWR); + if (fd < 0) { + err = errno; + error("Can't open input device: %s (%d)", + strerror(err), err); + return -err; + } + } + } + + memset(&dev, 0, sizeof(dev)); + if (name) + strncpy(dev.name, name, UINPUT_MAX_NAME_SIZE); + + dev.id.bustype = BUS_BLUETOOTH; + dev.id.vendor = 0x0000; + dev.id.product = 0x0000; + dev.id.version = 0x0000; + + if (write(fd, &dev, sizeof(dev)) < 0) { + err = errno; + error("Can't write device information: %s (%d)", + strerror(err), err); + close(fd); + errno = err; + return -err; + } + + ioctl(fd, UI_SET_EVBIT, EV_KEY); + ioctl(fd, UI_SET_EVBIT, EV_REL); + ioctl(fd, UI_SET_EVBIT, EV_REP); + + ioctl(fd, UI_SET_KEYBIT, KEY_PLAYPAUSE); + ioctl(fd, UI_SET_KEYBIT, KEY_STOP); + ioctl(fd, UI_SET_KEYBIT, KEY_NEXT); + ioctl(fd, UI_SET_KEYBIT, KEY_PREVIOUS); + ioctl(fd, UI_SET_KEYBIT, KEY_REWIND); + ioctl(fd, UI_SET_KEYBIT, KEY_FORWARD); + + if (ioctl(fd, UI_DEV_CREATE, NULL) < 0) { + err = errno; + error("Can't create uinput device: %s (%d)", + strerror(err), err); + close(fd); + errno = err; + return -err; + } + + return fd; +} + static struct avctp *avctp_get(bdaddr_t *src, bdaddr_t *dst) { struct avctp *session; @@ -406,6 +480,7 @@ static struct avctp *avctp_get(bdaddr_t *src, bdaddr_t *dst) session = g_new0(struct avctp, 1); + session->uinput = -1; session->sock = -1; bacpy(&session->src, src); bacpy(&session->dst, dst); @@ -415,33 +490,88 @@ static struct avctp *avctp_get(bdaddr_t *src, bdaddr_t *dst) return session; } -static void handle_panel_passthrough(const unsigned char *operands, int operand_count) +static void init_uinput(struct avctp *session) +{ + char address[18], *name; + + ba2str(&session->dst, address); + + name = session->dev->name ? session->dev->name : address; + + session->uinput = uinput_create(name); + if (session->uinput < 0) + error("AVRCP: failed to init uinput for %s", name); + else + debug("AVRCP: uinput initialized for %s", name); +} + +static int send_event(int fd, uint16_t type, uint16_t code, int32_t value) +{ + struct uinput_event event; + + memset(&event, 0, sizeof(event)); + event.type = type; + event.code = code; + event.value = value; + + return write(fd, &event, sizeof(event)); +} + +static void send_key(int fd, uint16_t key, int pressed) +{ + if (fd < 0) + return; + + send_event(fd, EV_KEY, key, pressed); + send_event(fd, EV_SYN, SYN_REPORT, 0); +} + +static void handle_panel_passthrough(struct avctp *session, + const unsigned char *operands, + int operand_count) { const char *status; + int pressed; if (operand_count == 0) return; - if (operands[0] & 0x80) + if (operands[0] & 0x80) { status = "released"; - else + pressed = 0; + } else { status = "pressed"; + pressed = 1; + } switch (operands[0] & 0x7F) { case PLAY_OP: debug("AVRCP: PLAY %s", status); + send_key(session->uinput, KEY_PLAYPAUSE, pressed); break; case STOP_OP: debug("AVRCP: STOP %s", status); + send_key(session->uinput, KEY_STOP, pressed); break; case PAUSE_OP: debug("AVRCP: PAUSE %s", status); + send_key(session->uinput, KEY_PLAYPAUSE, pressed); break; case NEXT_OP: debug("AVRCP: NEXT %s", status); + send_key(session->uinput, KEY_NEXT, pressed); break; case PREV_OP: debug("AVRCP: PREV %s", status); + send_key(session->uinput, KEY_PREVIOUS, pressed); + break; + case REWIND_OP: + debug("AVRCP: REWIND %s", status); + send_key(session->uinput, KEY_REWIND, pressed); + break; + case FAST_FORWARD_OP: + debug("AVRCP: FAST FORWARD %s", status); + send_key(session->uinput, KEY_FORWARD, pressed); break; default: debug("AVRCP: unknown button 0x%02X %s", operands[0] & 0x7F, status); @@ -506,7 +636,7 @@ static gboolean session_cb(GIOChannel *chan, GIOCondition cond, avrcp->code == CTYPE_CONTROL && avrcp->subunit_type == SUBUNIT_PANEL && avrcp->opcode == OP_PASSTHROUGH) { - handle_panel_passthrough(operands, operand_count); + handle_panel_passthrough(session, operands, operand_count); avctp->cr = AVCTP_RESPONSE; avrcp->code = CTYPE_ACCEPTED; ret = write(session->sock, buf, packet_size); @@ -553,6 +683,7 @@ static void auth_cb(DBusPendingCall *call, void *data) session->dev = manager_device_connected(&session->dst, AVRCP_TARGET_UUID); session->dev->control->session = session; + init_uinput(session); dbus_connection_emit_signal(session->dev->conn, session->dev->path, AUDIO_CONTROL_INTERFACE, "Connected", @@ -623,6 +754,12 @@ static gboolean avctp_server_cb(GIOChannel *chan, GIOCondition cond, void *data) session = avctp_get(&src, &dst); + if (!session) { + error("Unable to create new AVCTP session"); + close(cli_sk); + return TRUE; + } + if (session->sock >= 0) { error("Refusing unexpected connect from %s", address); close(cli_sk); @@ -651,6 +788,7 @@ proceed: session->dev = manager_device_connected(&dst, AVRCP_TARGET_UUID); session->dev->control->session = session; + init_uinput(session); flags |= G_IO_IN; dbus_connection_emit_signal(session->dev->conn, session->dev->path, @@ -712,6 +850,8 @@ static gboolean avctp_connect_cb(GIOChannel *chan, GIOCondition cond, goto failed; } + init_uinput(session); + dbus_connection_emit_signal(session->dev->conn, session->dev->path, AUDIO_CONTROL_INTERFACE, "Connected", DBUS_TYPE_INVALID); @@ -744,6 +884,11 @@ gboolean avrcp_connect(struct device *dev) return TRUE; session = avctp_get(&dev->src, &dev->dst); + if (!session) { + error("Unable to create new AVCTP session"); + return FALSE; + } + session->dev = dev; memset(&l2a, 0, sizeof(l2a)); -- cgit From 2bb9cae4a1c2fa8398452c5c7032d77853238cd8 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 25 Oct 2007 13:25:41 +0000 Subject: Fix L2CAP socket leak --- audio/control.c | 1 + 1 file changed, 1 insertion(+) (limited to 'audio/control.c') diff --git a/audio/control.c b/audio/control.c index bd100331..3f512672 100644 --- a/audio/control.c +++ b/audio/control.c @@ -443,6 +443,7 @@ static int uinput_create(char *name) ioctl(fd, UI_SET_EVBIT, EV_KEY); ioctl(fd, UI_SET_EVBIT, EV_REL); ioctl(fd, UI_SET_EVBIT, EV_REP); + ioctl(fd, UI_SET_EVBIT, EV_SYN); ioctl(fd, UI_SET_KEYBIT, KEY_PLAYPAUSE); ioctl(fd, UI_SET_KEYBIT, KEY_STOP); -- cgit From eb1366556817d77e611bf42990db1c74fae37b90 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 27 Nov 2007 09:15:16 +0000 Subject: Use KEY_PREVIOUSSONG and KEY_NEXTSONG (instead of KEY_PREVIOUS and KEY_NEXT) since they seem to be more widely supported by existing software --- audio/control.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'audio/control.c') diff --git a/audio/control.c b/audio/control.c index 3f512672..bb3f2bf1 100644 --- a/audio/control.c +++ b/audio/control.c @@ -447,8 +447,8 @@ static int uinput_create(char *name) ioctl(fd, UI_SET_KEYBIT, KEY_PLAYPAUSE); ioctl(fd, UI_SET_KEYBIT, KEY_STOP); - ioctl(fd, UI_SET_KEYBIT, KEY_NEXT); - ioctl(fd, UI_SET_KEYBIT, KEY_PREVIOUS); + ioctl(fd, UI_SET_KEYBIT, KEY_NEXTSONG); + ioctl(fd, UI_SET_KEYBIT, KEY_PREVIOUSSONG); ioctl(fd, UI_SET_KEYBIT, KEY_REWIND); ioctl(fd, UI_SET_KEYBIT, KEY_FORWARD); @@ -560,11 +560,11 @@ static void handle_panel_passthrough(struct avctp *session, break; case NEXT_OP: debug("AVRCP: NEXT %s", status); - send_key(session->uinput, KEY_NEXT, pressed); + send_key(session->uinput, KEY_NEXTSONG, pressed); break; case PREV_OP: debug("AVRCP: PREV %s", status); - send_key(session->uinput, KEY_PREVIOUS, pressed); + send_key(session->uinput, KEY_PREVIOUSSONG, pressed); break; case REWIND_OP: debug("AVRCP: REWIND %s", status); -- cgit From 68fdcb7d84d0d700f10ca3be42f9c7e51cfca932 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 16 Jan 2008 14:44:20 +0000 Subject: Add basic support for UNITINFO and SUBUNITINFO --- audio/control.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'audio/control.c') diff --git a/audio/control.c b/audio/control.c index bb3f2bf1..3c14c230 100644 --- a/audio/control.c +++ b/audio/control.c @@ -643,6 +643,19 @@ static gboolean session_cb(GIOChannel *chan, GIOCondition cond, ret = write(session->sock, buf, packet_size); } + if (avctp->packet_type == AVCTP_PACKET_SINGLE && + avctp->cr == AVCTP_COMMAND && + avctp->pid == htons(AV_REMOTE_SVCLASS_ID) && + avrcp->code == CTYPE_STATUS && + (avrcp->opcode == OP_UNITINFO + || avrcp->opcode == OP_SUBUNITINFO)) { + avctp->cr = AVCTP_RESPONSE; + avrcp->code = CTYPE_STABLE; + debug("reply to %s", avrcp->opcode == OP_UNITINFO ? + "OP_UNITINFO" : "OP_SUBUNITINFO"); + ret = write(session->sock, buf, packet_size); + } + return TRUE; failed: -- cgit From 4eb241042f5f4f93c1b772398f1e3353394b6d8b Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 30 Jan 2008 11:57:54 +0000 Subject: Add extra check for session->dev == NULL before accessing it --- audio/control.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'audio/control.c') diff --git a/audio/control.c b/audio/control.c index 3c14c230..9b062fbd 100644 --- a/audio/control.c +++ b/audio/control.c @@ -393,7 +393,8 @@ static void avctp_unref(struct avctp *session) if (session->io) g_source_remove(session->io); - session->dev->control->session = NULL; + if (session->dev) + session->dev->control->session = NULL; if (session->uinput >= 0) { ioctl(session->uinput, UI_DEV_DESTROY); -- cgit From e823c15e43a6f924779e466d434c51157002d9ee Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sat, 2 Feb 2008 03:37:05 +0000 Subject: Update copyright information --- audio/control.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'audio/control.c') diff --git a/audio/control.c b/audio/control.c index 9b062fbd..6085f587 100644 --- a/audio/control.c +++ b/audio/control.c @@ -3,7 +3,7 @@ * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2006-2007 Nokia Corporation - * Copyright (C) 2004-2007 Marcel Holtmann + * Copyright (C) 2004-2008 Marcel Holtmann * * * This program is free software; you can redistribute it and/or modify -- cgit From aa4ce3509e6bf62a20e89d680e2792388f0afde1 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 5 Mar 2008 17:43:54 +0000 Subject: Do master role switching by default and add ForceMaster config option --- audio/control.c | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) (limited to 'audio/control.c') diff --git a/audio/control.c b/audio/control.c index 6085f587..2c8f03ba 100644 --- a/audio/control.c +++ b/audio/control.c @@ -309,7 +309,7 @@ static int avrcp_tg_record(sdp_buf_t *buf) return ret; } -static GIOChannel *avctp_server_socket(void) +static GIOChannel *avctp_server_socket(gboolean master) { int sock, lm; struct sockaddr_l2 addr; @@ -322,6 +322,10 @@ static GIOChannel *avctp_server_socket(void) } lm = L2CAP_LM_SECURE; + + if (master) + lm |= L2CAP_LM_MASTER; + if (setsockopt(sock, SOL_L2CAP, L2CAP_LM, &lm, sizeof(lm)) < 0) { error("AVCTP server setsockopt: %s (%d)", strerror(errno), errno); close(sock); @@ -975,13 +979,25 @@ void avrcp_disconnect(struct device *dev) control->session = NULL; } -int avrcp_init(DBusConnection *conn) +int avrcp_init(DBusConnection *conn, GKeyFile *config) { sdp_buf_t buf; + gboolean tmp, master = TRUE; + GError *err = NULL; if (avctp_server) return 0; + + tmp = g_key_file_get_boolean(config, "General", "ForceMaster", + &err); + if (err) { + debug("audio.conf: %s", err->message); + g_error_free(err); + err = NULL; + } else + master = tmp; + connection = dbus_connection_ref(conn); if (avrcp_tg_record(&buf) < 0) { @@ -1010,7 +1026,7 @@ int avrcp_init(DBusConnection *conn) return -1; } - avctp_server = avctp_server_socket(); + avctp_server = avctp_server_socket(master); if (!avctp_server) return -1; -- cgit From ce8053e02539f99b025054c0055cf4d0ca1485e7 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 5 Mar 2008 17:51:48 +0000 Subject: Rename ForceMaster to just Master --- audio/control.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'audio/control.c') diff --git a/audio/control.c b/audio/control.c index 2c8f03ba..861241b5 100644 --- a/audio/control.c +++ b/audio/control.c @@ -989,7 +989,7 @@ int avrcp_init(DBusConnection *conn, GKeyFile *config) return 0; - tmp = g_key_file_get_boolean(config, "General", "ForceMaster", + tmp = g_key_file_get_boolean(config, "General", "Master", &err); if (err) { debug("audio.conf: %s", err->message); -- cgit From 401a54f27d7c93f19eeff26b35ce5a8b0e20a4f5 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 5 Mar 2008 17:54:58 +0000 Subject: Remove unnecessary empty line --- audio/control.c | 1 - 1 file changed, 1 deletion(-) (limited to 'audio/control.c') diff --git a/audio/control.c b/audio/control.c index 861241b5..63612f4a 100644 --- a/audio/control.c +++ b/audio/control.c @@ -988,7 +988,6 @@ int avrcp_init(DBusConnection *conn, GKeyFile *config) if (avctp_server) return 0; - tmp = g_key_file_get_boolean(config, "General", "Master", &err); if (err) { -- cgit From 7299869ac79b76564cd68411acb18f4233ddbbb7 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Thu, 27 Mar 2008 23:07:19 +0000 Subject: Convert audio service into a plugin. --- audio/control.c | 89 +++++++++++++++++++++++++-------------------------------- 1 file changed, 39 insertions(+), 50 deletions(-) (limited to 'audio/control.c') diff --git a/audio/control.c b/audio/control.c index 63612f4a..13b4b811 100644 --- a/audio/control.c +++ b/audio/control.c @@ -53,6 +53,7 @@ #include "manager.h" #include "avdtp.h" #include "control.h" +#include "sdpd.h" #define AVCTP_PSM 23 @@ -171,27 +172,28 @@ struct control { struct avctp *session; }; -static int avrcp_ct_record(sdp_buf_t *buf) +static sdp_record_t *avrcp_ct_record() { sdp_list_t *svclass_id, *pfseq, *apseq, *root; uuid_t root_uuid, l2cap, avctp, avrct; sdp_profile_desc_t profile[1]; sdp_list_t *aproto, *proto[2]; - sdp_record_t record; + sdp_record_t *record; sdp_data_t *psm, *version, *features; uint16_t lp = AVCTP_PSM, ver = 0x0103, feat = 0x000f; - int ret = 0; - memset(&record, 0, sizeof(sdp_record_t)); + record = sdp_record_alloc(); + if (!record) + return NULL; sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP); root = sdp_list_append(0, &root_uuid); - sdp_set_browse_groups(&record, root); + sdp_set_browse_groups(record, root); /* Service Class ID List */ sdp_uuid16_create(&avrct, AV_REMOTE_SVCLASS_ID); svclass_id = sdp_list_append(0, &avrct); - sdp_set_service_classes(&record, svclass_id); + sdp_set_service_classes(record, svclass_id); /* Protocol Descriptor List */ sdp_uuid16_create(&l2cap, L2CAP_UUID); @@ -207,23 +209,18 @@ static int avrcp_ct_record(sdp_buf_t *buf) apseq = sdp_list_append(apseq, proto[1]); aproto = sdp_list_append(0, apseq); - sdp_set_access_protos(&record, aproto); + sdp_set_access_protos(record, aproto); /* Bluetooth Profile Descriptor List */ sdp_uuid16_create(&profile[0].uuid, AV_REMOTE_PROFILE_ID); profile[0].version = ver; pfseq = sdp_list_append(0, &profile[0]); - sdp_set_profile_descs(&record, pfseq); + sdp_set_profile_descs(record, pfseq); features = sdp_data_alloc(SDP_UINT16, &feat); - sdp_attr_add(&record, SDP_ATTR_SUPPORTED_FEATURES, features); - - sdp_set_info_attr(&record, "AVRCP CT", 0, 0); + sdp_attr_add(record, SDP_ATTR_SUPPORTED_FEATURES, features); - if (sdp_gen_record_pdu(&record, buf) < 0) - ret = -1; - else - ret = 0; + sdp_set_info_attr(record, "AVRCP CT", 0, 0); free(psm); free(version); @@ -234,33 +231,32 @@ static int avrcp_ct_record(sdp_buf_t *buf) sdp_list_free(aproto, 0); sdp_list_free(root, 0); sdp_list_free(svclass_id, 0); - sdp_list_free(record.attrlist, (sdp_free_func_t) sdp_data_free); - sdp_list_free(record.pattern, free); - return ret; + return record; } -static int avrcp_tg_record(sdp_buf_t *buf) +static sdp_record_t *avrcp_tg_record() { sdp_list_t *svclass_id, *pfseq, *apseq, *root; uuid_t root_uuid, l2cap, avctp, avrtg; sdp_profile_desc_t profile[1]; sdp_list_t *aproto, *proto[2]; - sdp_record_t record; + sdp_record_t *record; sdp_data_t *psm, *version, *features; uint16_t lp = AVCTP_PSM, ver = 0x0103, feat = 0x000f; - int ret = 0; - memset(&record, 0, sizeof(sdp_record_t)); + record = sdp_record_alloc(); + if (!record) + return NULL; sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP); root = sdp_list_append(0, &root_uuid); - sdp_set_browse_groups(&record, root); + sdp_set_browse_groups(record, root); /* Service Class ID List */ sdp_uuid16_create(&avrtg, AV_REMOTE_TARGET_SVCLASS_ID); svclass_id = sdp_list_append(0, &avrtg); - sdp_set_service_classes(&record, svclass_id); + sdp_set_service_classes(record, svclass_id); /* Protocol Descriptor List */ sdp_uuid16_create(&l2cap, L2CAP_UUID); @@ -276,23 +272,18 @@ static int avrcp_tg_record(sdp_buf_t *buf) apseq = sdp_list_append(apseq, proto[1]); aproto = sdp_list_append(0, apseq); - sdp_set_access_protos(&record, aproto); + sdp_set_access_protos(record, aproto); /* Bluetooth Profile Descriptor List */ sdp_uuid16_create(&profile[0].uuid, AV_REMOTE_PROFILE_ID); profile[0].version = ver; pfseq = sdp_list_append(0, &profile[0]); - sdp_set_profile_descs(&record, pfseq); + sdp_set_profile_descs(record, pfseq); features = sdp_data_alloc(SDP_UINT16, &feat); - sdp_attr_add(&record, SDP_ATTR_SUPPORTED_FEATURES, features); + sdp_attr_add(record, SDP_ATTR_SUPPORTED_FEATURES, features); - sdp_set_info_attr(&record, "AVRCP TG", 0, 0); - - if (sdp_gen_record_pdu(&record, buf) < 0) - ret = -1; - else - ret = 0; + sdp_set_info_attr(record, "AVRCP TG", 0, 0); free(psm); free(version); @@ -303,10 +294,8 @@ static int avrcp_tg_record(sdp_buf_t *buf) sdp_list_free(pfseq, 0); sdp_list_free(root, 0); sdp_list_free(svclass_id, 0); - sdp_list_free(record.attrlist, (sdp_free_func_t) sdp_data_free); - sdp_list_free(record.pattern, free); - return ret; + return record; } static GIOChannel *avctp_server_socket(gboolean master) @@ -981,7 +970,7 @@ void avrcp_disconnect(struct device *dev) int avrcp_init(DBusConnection *conn, GKeyFile *config) { - sdp_buf_t buf; + sdp_record_t *record; gboolean tmp, master = TRUE; GError *err = NULL; @@ -999,31 +988,31 @@ int avrcp_init(DBusConnection *conn, GKeyFile *config) connection = dbus_connection_ref(conn); - if (avrcp_tg_record(&buf) < 0) { + record = avrcp_tg_record(); + if (!record) { error("Unable to allocate new service record"); return -1; } - tg_record_id = add_service_record(conn, &buf); - free(buf.data); - - if (!tg_record_id) { + if (add_record_to_server(BDADDR_ANY, record) < 0) { error("Unable to register AVRCP target service record"); + sdp_record_free(record); return -1; } + tg_record_id = record->handle; - if (avrcp_ct_record(&buf) < 0) { + record = avrcp_ct_record(); + if (!record) { error("Unable to allocate new service record"); return -1; } - ct_record_id = add_service_record(conn, &buf); - free(buf.data); - - if (!ct_record_id) { + if (add_record_to_server(BDADDR_ANY, record) < 0) { error("Unable to register AVRCP controller service record"); + sdp_record_free(record); return -1; } + ct_record_id = record->handle; avctp_server = avctp_server_socket(master); if (!avctp_server) @@ -1044,11 +1033,11 @@ void avrcp_exit(void) g_io_channel_unref(avctp_server); avctp_server = NULL; - remove_service_record(connection, ct_record_id); + remove_record_from_server(ct_record_id); ct_record_id = 0; - remove_service_record(connection, ct_record_id); - ct_record_id = 0; + remove_record_from_server(tg_record_id); + tg_record_id = 0; dbus_connection_unref(connection); connection = NULL; -- cgit From 15892632de70a6ed6a7025a09a11b9e287f8f68c Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Mon, 5 May 2008 14:46:39 +0000 Subject: Fixes avdtp and avctp authorization for incoming connections. --- audio/control.c | 55 ++++++++++++++++++++++++++++++------------------------- 1 file changed, 30 insertions(+), 25 deletions(-) (limited to 'audio/control.c') diff --git a/audio/control.c b/audio/control.c index 13b4b811..1ed7d62f 100644 --- a/audio/control.c +++ b/audio/control.c @@ -47,6 +47,7 @@ #include "dbus.h" #include "dbus-helper.h" +#include "dbus-service.h" #include "logging.h" #include "uinput.h" #include "device.h" @@ -657,32 +658,22 @@ failed: avctp_unref(session); return FALSE; } - -static void auth_cb(DBusPendingCall *call, void *data) +static void auth_cb(DBusError *derr, void *user_data) { + struct avctp *session = user_data; GIOChannel *io; - struct avctp *session = data; - DBusMessage *reply = dbus_pending_call_steal_reply(call); - DBusError err; - dbus_pending_call_unref(session->pending_auth); - session->pending_auth = NULL; - - dbus_error_init(&err); - if (dbus_set_error_from_message(&err, reply)) { - error("Access denied: %s", err.message); - - if (dbus_error_has_name(&err, DBUS_ERROR_NO_REPLY)) { + if (derr && dbus_error_is_set(derr)) { + error("Access denied: %s", derr->message); + if (dbus_error_has_name(derr, DBUS_ERROR_NO_REPLY)) { debug("Canceling authorization request"); - manager_cancel_authorize(&session->dst, + if (service_cancel_auth(&session->dst) < 0) + manager_cancel_authorize(&session->dst, AVRCP_TARGET_UUID, NULL); } avctp_unref(session); - - dbus_message_unref(reply); - return; } @@ -706,6 +697,19 @@ static void auth_cb(DBusPendingCall *call, void *data) g_io_channel_unref(io); } +static void auth_cb_old(DBusPendingCall *call, void *data) +{ + DBusMessage *reply = dbus_pending_call_steal_reply(call); + DBusError err; + + dbus_error_init(&err); + dbus_set_error_from_message(&err, reply); + auth_cb(&err, data); + dbus_error_free(&err); + + dbus_message_unref(reply); +} + static gboolean avctp_server_cb(GIOChannel *chan, GIOCondition cond, void *data) { int srv_sk, cli_sk; @@ -775,11 +779,19 @@ static gboolean avctp_server_cb(GIOChannel *chan, GIOCondition cond, void *data) } session->state = AVCTP_STATE_CONNECTING; + session->mtu = l2o.imtu; + session->sock = cli_sk; + + io = g_io_channel_unix_new(session->sock); + session->io = g_io_add_watch(io, flags, (GIOFunc) session_cb, session); + g_io_channel_unref(io); if (avdtp_is_connected(&src, &dst)) goto proceed; - if (!manager_authorize(&dst, AVRCP_TARGET_UUID, auth_cb, session, + if (service_req_auth(&src, &dst, AVRCP_TARGET_UUID, auth_cb, session) == 0) + goto proceed; + else if (!manager_authorize(&dst, AVRCP_TARGET_UUID, auth_cb_old, session, &session->pending_auth)) { close(cli_sk); avctp_unref(session); @@ -787,10 +799,6 @@ static gboolean avctp_server_cb(GIOChannel *chan, GIOCondition cond, void *data) } proceed: - session->mtu = l2o.imtu; - session->sock = cli_sk; - - io = g_io_channel_unix_new(session->sock); if (!session->pending_auth) { session->state = AVCTP_STATE_CONNECTED; session->dev = manager_device_connected(&dst, @@ -805,9 +813,6 @@ proceed: DBUS_TYPE_INVALID); } - session->io = g_io_add_watch(io, flags, (GIOFunc) session_cb, session); - g_io_channel_unref(io); - return TRUE; } -- cgit From e3dede624b3f622fdb176b140e057f98cf26ca2b Mon Sep 17 00:00:00 2001 From: Claudio Takahasi Date: Thu, 8 May 2008 16:59:55 +0000 Subject: fixed format string --- audio/control.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'audio/control.c') diff --git a/audio/control.c b/audio/control.c index 1ed7d62f..6007c82b 100644 --- a/audio/control.c +++ b/audio/control.c @@ -328,13 +328,13 @@ static GIOChannel *avctp_server_socket(gboolean master) addr.l2_psm = htobs(AVCTP_PSM); if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - error("AVCTP server bind: %s", strerror(errno), errno); + error("AVCTP server bind: %s (%d)", strerror(errno), errno); close(sock); return NULL; } if (listen(sock, 4) < 0) { - error("AVCTP server listen: %s", strerror(errno), errno); + error("AVCTP server listen: %s (%d)", strerror(errno), errno); close(sock); return NULL; } -- cgit From 4d39e4d2ea552e2d4ad36c085700f513e10be7e0 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Thu, 8 May 2008 18:47:21 +0000 Subject: Move set_nonblocking() to the GLib helpers for now --- audio/control.c | 1 + 1 file changed, 1 insertion(+) (limited to 'audio/control.c') diff --git a/audio/control.c b/audio/control.c index 6007c82b..a6976d05 100644 --- a/audio/control.c +++ b/audio/control.c @@ -55,6 +55,7 @@ #include "avdtp.h" #include "control.h" #include "sdpd.h" +#include "glib-helper.h" #define AVCTP_PSM 23 -- cgit From e7d668ac9e813bc9922ee7d771848bd8822d5d1f Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Thu, 8 May 2008 20:23:45 +0000 Subject: Move D-Bus watch functions into libgdbus --- audio/control.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'audio/control.c') diff --git a/audio/control.c b/audio/control.c index a6976d05..cffcf517 100644 --- a/audio/control.c +++ b/audio/control.c @@ -37,15 +37,14 @@ #include #include -#include -#include - #include #include #include #include -#include "dbus.h" +#include +#include + #include "dbus-helper.h" #include "dbus-service.h" #include "logging.h" -- cgit From 15ea15b3a752f0487bc50d0ea04925f1b9d33dcb Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Thu, 8 May 2008 22:19:14 +0000 Subject: Move D-Bus object and interface helpers into libgdbus --- audio/control.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'audio/control.c') diff --git a/audio/control.c b/audio/control.c index cffcf517..f79eee9c 100644 --- a/audio/control.c +++ b/audio/control.c @@ -44,8 +44,8 @@ #include #include +#include -#include "dbus-helper.h" #include "dbus-service.h" #include "logging.h" #include "uinput.h" -- cgit From a59f82bd8a01e83bd7724beccfe2a0383982650c Mon Sep 17 00:00:00 2001 From: Cidorvan Leite Date: Fri, 9 May 2008 13:18:47 +0000 Subject: AVCTP l2cap connection clean up --- audio/control.c | 114 ++++++++++++-------------------------------------------- 1 file changed, 23 insertions(+), 91 deletions(-) (limited to 'audio/control.c') diff --git a/audio/control.c b/audio/control.c index f79eee9c..1777899e 100644 --- a/audio/control.c +++ b/audio/control.c @@ -816,51 +816,36 @@ proceed: return TRUE; } -static gboolean avctp_connect_cb(GIOChannel *chan, GIOCondition cond, - gpointer data) +static void avctp_connect_cb(GIOChannel *chan, int err, gpointer data) { struct avctp *session = data; struct l2cap_options l2o; socklen_t len; - int ret, err, sk; + int sk; char address[18]; - if (cond & G_IO_NVAL) - return FALSE; - - sk = g_io_channel_unix_get_fd(chan); - - ba2str(&session->dst, address); - - len = sizeof(ret); - if (getsockopt(sk, SOL_SOCKET, SO_ERROR, &ret, &len) < 0) { - err = errno; - error("getsockopt(SO_ERROR): %s (%d)", strerror(err), err); - goto failed; - } - - if (ret != 0) { - err = ret; - error("AVCTP connect(%s): %s (%d)", address, strerror(err), - err); - goto failed; - } - - if (cond & G_IO_HUP) { - err = EIO; - goto failed; + if (err < 0) { + avctp_unref(session); + error("AVCTP connect(%s): %s (%d)", address, strerror(-err), + -err); + return; } + ba2str(&session->dst, address); debug("AVCTP: connected to %s", address); + g_io_channel_set_close_on_unref(chan, FALSE); + sk = g_io_channel_unix_get_fd(chan); + session->sock = sk; + memset(&l2o, 0, sizeof(l2o)); len = sizeof(l2o); - if (getsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &l2o, - &len) < 0) { + if (getsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &l2o, &len) < 0) { err = errno; + avctp_unref(session); error("getsockopt(L2CAP_OPTIONS): %s (%d)", strerror(err), err); - goto failed; + return; } init_uinput(session); @@ -875,23 +860,13 @@ static gboolean avctp_connect_cb(GIOChannel *chan, GIOCondition cond, session->io = g_io_add_watch(chan, G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL, (GIOFunc) session_cb, session); - return FALSE; - -failed: - close(sk); - - avctp_unref(session); - - return FALSE; } gboolean avrcp_connect(struct device *dev) { struct control *control = dev->control; struct avctp *session; - struct sockaddr_l2 l2a; - GIOChannel *io; - int sk; + int err; if (control->session) return TRUE; @@ -903,62 +878,19 @@ gboolean avrcp_connect(struct device *dev) } session->dev = dev; + session->state = AVCTP_STATE_CONNECTING; - memset(&l2a, 0, sizeof(l2a)); - l2a.l2_family = AF_BLUETOOTH; - bacpy(&l2a.l2_bdaddr, &dev->src); - - sk = socket(AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP); - if (sk < 0) { - error("Cannot create L2CAP socket. %s(%d)", strerror(errno), - errno); - goto failed; - } - - if (bind(sk, (struct sockaddr *) &l2a, sizeof(l2a)) < 0) { - error("Bind failed. %s (%d)", strerror(errno), errno); - goto failed; - } - - memset(&l2a, 0, sizeof(l2a)); - l2a.l2_family = AF_BLUETOOTH; - bacpy(&l2a.l2_bdaddr, &dev->dst); - l2a.l2_psm = htobs(AVCTP_PSM); - - if (set_nonblocking(sk) < 0) { - error("Set non blocking: %s (%d)", strerror(errno), errno); - goto failed; + err = bt_l2cap_connect(&dev->src, &dev->dst, AVCTP_PSM, 0, + avctp_connect_cb, session); + if (err < 0) { + avctp_unref(session); + error("Connect failed. %s(%d)", strerror(-err), -err); + return FALSE; } - io = g_io_channel_unix_new(sk); - g_io_channel_set_close_on_unref(io, FALSE); - session->sock = sk; - - if (connect(sk, (struct sockaddr *) &l2a, sizeof(l2a)) < 0) { - if (!(errno == EAGAIN || errno == EINPROGRESS)) { - error("Connect failed. %s(%d)", strerror(errno), - errno); - g_io_channel_close(io); - g_io_channel_unref(io); - goto failed; - } - - session->state = AVCTP_STATE_CONNECTING; - - g_io_add_watch(io, G_IO_OUT | G_IO_HUP | G_IO_ERR | G_IO_NVAL, - (GIOFunc) avctp_connect_cb, session); - } else - avctp_connect_cb(io, G_IO_OUT, session); - - g_io_channel_unref(io); - control->session = session; return TRUE; - -failed: - avctp_unref(session); - return FALSE; } void avrcp_disconnect(struct device *dev) -- cgit From 0094809955895c974fbe95f2d3ed13f420a6a6ed Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Wed, 14 May 2008 22:16:16 +0000 Subject: Make bt_io_callback_t to take both source and destination. --- audio/control.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'audio/control.c') diff --git a/audio/control.c b/audio/control.c index 1777899e..6bf05bef 100644 --- a/audio/control.c +++ b/audio/control.c @@ -816,7 +816,8 @@ proceed: return TRUE; } -static void avctp_connect_cb(GIOChannel *chan, int err, gpointer data) +static void avctp_connect_cb(GIOChannel *chan, int err, const bdaddr_t *src, + const bdaddr_t *dst, gpointer data) { struct avctp *session = data; struct l2cap_options l2o; -- cgit From 067f966a9b5ed7e8e28b5f2b483fb4650da9de88 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Thu, 15 May 2008 02:51:55 +0000 Subject: Fix default value handling when config file is not present --- audio/control.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) (limited to 'audio/control.c') diff --git a/audio/control.c b/audio/control.c index 6bf05bef..4e8b948d 100644 --- a/audio/control.c +++ b/audio/control.c @@ -915,14 +915,15 @@ int avrcp_init(DBusConnection *conn, GKeyFile *config) if (avctp_server) return 0; - tmp = g_key_file_get_boolean(config, "General", "Master", - &err); - if (err) { - debug("audio.conf: %s", err->message); - g_error_free(err); - err = NULL; - } else - master = tmp; + if (config) { + tmp = g_key_file_get_boolean(config, "General", + "Master", &err); + if (err) { + debug("audio.conf: %s", err->message); + g_error_free(err); + } else + master = tmp; + } connection = dbus_connection_ref(conn); -- cgit From 8162199a0b96cb8d41196c65824b55121256e9df Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Tue, 27 May 2008 14:02:41 +0000 Subject: Make control.c to use bt_l2cap_listen. --- audio/control.c | 392 ++++++++++++++++++++++++-------------------------------- 1 file changed, 165 insertions(+), 227 deletions(-) (limited to 'audio/control.c') diff --git a/audio/control.c b/audio/control.c index 4e8b948d..9dfcd46d 100644 --- a/audio/control.c +++ b/audio/control.c @@ -299,57 +299,7 @@ static sdp_record_t *avrcp_tg_record() return record; } -static GIOChannel *avctp_server_socket(gboolean master) -{ - int sock, lm; - struct sockaddr_l2 addr; - GIOChannel *io; - - sock = socket(AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP); - if (sock < 0) { - error("AVCTP server socket: %s (%d)", strerror(errno), errno); - return NULL; - } - - lm = L2CAP_LM_SECURE; - - if (master) - lm |= L2CAP_LM_MASTER; - - if (setsockopt(sock, SOL_L2CAP, L2CAP_LM, &lm, sizeof(lm)) < 0) { - error("AVCTP server setsockopt: %s (%d)", strerror(errno), errno); - close(sock); - return NULL; - } - - memset(&addr, 0, sizeof(addr)); - addr.l2_family = AF_BLUETOOTH; - bacpy(&addr.l2_bdaddr, BDADDR_ANY); - addr.l2_psm = htobs(AVCTP_PSM); - - if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - error("AVCTP server bind: %s (%d)", strerror(errno), errno); - close(sock); - return NULL; - } - - if (listen(sock, 4) < 0) { - error("AVCTP server listen: %s (%d)", strerror(errno), errno); - close(sock); - return NULL; - } - - io = g_io_channel_unix_new(sock); - if (!io) { - error("Unable to allocate new io channel"); - close(sock); - return NULL; - } - - return io; -} - -static struct avctp *find_session(bdaddr_t *src, bdaddr_t *dst) +static struct avctp *find_session(const bdaddr_t *src, const bdaddr_t *dst) { GSList *l; @@ -365,101 +315,7 @@ static struct avctp *find_session(bdaddr_t *src, bdaddr_t *dst) return NULL; } -static void avctp_unref(struct avctp *session) -{ - sessions = g_slist_remove(sessions, session); - - if (session->pending_auth) { - manager_cancel_authorize(&session->dst, AVRCP_TARGET_UUID, - NULL); - dbus_pending_call_cancel(session->pending_auth); - dbus_pending_call_unref(session->pending_auth); - } - - if (session->state == AVCTP_STATE_CONNECTED) - dbus_connection_emit_signal(session->dev->conn, - session->dev->path, - AUDIO_CONTROL_INTERFACE, - "Disconnected", - DBUS_TYPE_INVALID); - if (session->sock >= 0) - close(session->sock); - if (session->io) - g_source_remove(session->io); - - if (session->dev) - session->dev->control->session = NULL; - - if (session->uinput >= 0) { - ioctl(session->uinput, UI_DEV_DESTROY); - close(session->uinput); - } - - g_free(session); -} - -static int uinput_create(char *name) -{ - struct uinput_dev dev; - int fd, err; - - fd = open("/dev/uinput", O_RDWR); - if (fd < 0) { - fd = open("/dev/input/uinput", O_RDWR); - if (fd < 0) { - fd = open("/dev/misc/uinput", O_RDWR); - if (fd < 0) { - err = errno; - error("Can't open input device: %s (%d)", - strerror(err), err); - return -err; - } - } - } - - memset(&dev, 0, sizeof(dev)); - if (name) - strncpy(dev.name, name, UINPUT_MAX_NAME_SIZE); - - dev.id.bustype = BUS_BLUETOOTH; - dev.id.vendor = 0x0000; - dev.id.product = 0x0000; - dev.id.version = 0x0000; - - if (write(fd, &dev, sizeof(dev)) < 0) { - err = errno; - error("Can't write device information: %s (%d)", - strerror(err), err); - close(fd); - errno = err; - return -err; - } - - ioctl(fd, UI_SET_EVBIT, EV_KEY); - ioctl(fd, UI_SET_EVBIT, EV_REL); - ioctl(fd, UI_SET_EVBIT, EV_REP); - ioctl(fd, UI_SET_EVBIT, EV_SYN); - - ioctl(fd, UI_SET_KEYBIT, KEY_PLAYPAUSE); - ioctl(fd, UI_SET_KEYBIT, KEY_STOP); - ioctl(fd, UI_SET_KEYBIT, KEY_NEXTSONG); - ioctl(fd, UI_SET_KEYBIT, KEY_PREVIOUSSONG); - ioctl(fd, UI_SET_KEYBIT, KEY_REWIND); - ioctl(fd, UI_SET_KEYBIT, KEY_FORWARD); - - if (ioctl(fd, UI_DEV_CREATE, NULL) < 0) { - err = errno; - error("Can't create uinput device: %s (%d)", - strerror(err), err); - close(fd); - errno = err; - return -err; - } - - return fd; -} - -static struct avctp *avctp_get(bdaddr_t *src, bdaddr_t *dst) +static struct avctp *avctp_get(const bdaddr_t *src, const bdaddr_t *dst) { struct avctp *session; @@ -486,21 +342,6 @@ static struct avctp *avctp_get(bdaddr_t *src, bdaddr_t *dst) return session; } -static void init_uinput(struct avctp *session) -{ - char address[18], *name; - - ba2str(&session->dst, address); - - name = session->dev->name ? session->dev->name : address; - - session->uinput = uinput_create(name); - if (session->uinput < 0) - error("AVRCP: failed to init uinput for %s", name); - else - debug("AVRCP: uinput initialized for %s", name); -} - static int send_event(int fd, uint16_t type, uint16_t code, int32_t value) { struct uinput_event event; @@ -575,6 +416,39 @@ static void handle_panel_passthrough(struct avctp *session, } } +static void avctp_unref(struct avctp *session) +{ + sessions = g_slist_remove(sessions, session); + + if (session->pending_auth) { + manager_cancel_authorize(&session->dst, AVRCP_TARGET_UUID, + NULL); + dbus_pending_call_cancel(session->pending_auth); + dbus_pending_call_unref(session->pending_auth); + } + + if (session->state == AVCTP_STATE_CONNECTED) + dbus_connection_emit_signal(session->dev->conn, + session->dev->path, + AUDIO_CONTROL_INTERFACE, + "Disconnected", + DBUS_TYPE_INVALID); + if (session->sock >= 0) + close(session->sock); + if (session->io) + g_source_remove(session->io); + + if (session->dev) + session->dev->control->session = NULL; + + if (session->uinput >= 0) { + ioctl(session->uinput, UI_DEV_DESTROY); + close(session->uinput); + } + + g_free(session); +} + static gboolean session_cb(GIOChannel *chan, GIOCondition cond, gpointer data) { @@ -658,6 +532,83 @@ failed: avctp_unref(session); return FALSE; } + +static int uinput_create(char *name) +{ + struct uinput_dev dev; + int fd, err; + + fd = open("/dev/uinput", O_RDWR); + if (fd < 0) { + fd = open("/dev/input/uinput", O_RDWR); + if (fd < 0) { + fd = open("/dev/misc/uinput", O_RDWR); + if (fd < 0) { + err = errno; + error("Can't open input device: %s (%d)", + strerror(err), err); + return -err; + } + } + } + + memset(&dev, 0, sizeof(dev)); + if (name) + strncpy(dev.name, name, UINPUT_MAX_NAME_SIZE); + + dev.id.bustype = BUS_BLUETOOTH; + dev.id.vendor = 0x0000; + dev.id.product = 0x0000; + dev.id.version = 0x0000; + + if (write(fd, &dev, sizeof(dev)) < 0) { + err = errno; + error("Can't write device information: %s (%d)", + strerror(err), err); + close(fd); + errno = err; + return -err; + } + + ioctl(fd, UI_SET_EVBIT, EV_KEY); + ioctl(fd, UI_SET_EVBIT, EV_REL); + ioctl(fd, UI_SET_EVBIT, EV_REP); + ioctl(fd, UI_SET_EVBIT, EV_SYN); + + ioctl(fd, UI_SET_KEYBIT, KEY_PLAYPAUSE); + ioctl(fd, UI_SET_KEYBIT, KEY_STOP); + ioctl(fd, UI_SET_KEYBIT, KEY_NEXTSONG); + ioctl(fd, UI_SET_KEYBIT, KEY_PREVIOUSSONG); + ioctl(fd, UI_SET_KEYBIT, KEY_REWIND); + ioctl(fd, UI_SET_KEYBIT, KEY_FORWARD); + + if (ioctl(fd, UI_DEV_CREATE, NULL) < 0) { + err = errno; + error("Can't create uinput device: %s (%d)", + strerror(err), err); + close(fd); + errno = err; + return -err; + } + + return fd; +} + +static void init_uinput(struct avctp *session) +{ + char address[18], *name; + + ba2str(&session->dst, address); + + name = session->dev->name ? session->dev->name : address; + + session->uinput = uinput_create(name); + if (session->uinput < 0) + error("AVRCP: failed to init uinput for %s", name); + else + debug("AVRCP: uinput initialized for %s", name); +} + static void auth_cb(DBusError *derr, void *user_data) { struct avctp *session = user_data; @@ -710,98 +661,65 @@ static void auth_cb_old(DBusPendingCall *call, void *data) dbus_message_unref(reply); } -static gboolean avctp_server_cb(GIOChannel *chan, GIOCondition cond, void *data) +static void avctp_server_cb(GIOChannel *chan, int err, const bdaddr_t *src, + const bdaddr_t *dst, gpointer data) { - int srv_sk, cli_sk; socklen_t size; - struct sockaddr_l2 addr; struct l2cap_options l2o; - bdaddr_t src, dst; struct avctp *session; - GIOChannel *io; GIOCondition flags = G_IO_ERR | G_IO_HUP | G_IO_NVAL; char address[18]; - if (cond & G_IO_NVAL) - return FALSE; - - if (cond & (G_IO_HUP | G_IO_ERR)) { - error("Hangup or error on AVCTP server socket"); - g_io_channel_close(chan); - raise(SIGTERM); - return FALSE; - } - - srv_sk = g_io_channel_unix_get_fd(chan); - - size = sizeof(struct sockaddr_l2); - cli_sk = accept(srv_sk, (struct sockaddr *) &addr, &size); - if (cli_sk < 0) { - error("AVCTP accept: %s (%d)", strerror(errno), errno); - return TRUE; - } - - bacpy(&dst, &addr.l2_bdaddr); - - ba2str(&dst, address); - debug("AVCTP: incoming connect from %s", address); - - size = sizeof(struct sockaddr_l2); - if (getsockname(cli_sk, (struct sockaddr *) &addr, &size) < 0) { - error("getsockname: %s (%d)", strerror(errno), errno); - close(cli_sk); - return TRUE; - } - - bacpy(&src, &addr.l2_bdaddr); - - memset(&l2o, 0, sizeof(l2o)); - size = sizeof(l2o); - if (getsockopt(cli_sk, SOL_L2CAP, L2CAP_OPTIONS, &l2o, &size) < 0) { - error("getsockopt(L2CAP_OPTIONS): %s (%d)", strerror(errno), - errno); - close(cli_sk); - return TRUE; + if (err < 0) { + error("AVCTP server socket: %s (%d)", strerror(-err), -err); + return; } - session = avctp_get(&src, &dst); + session = avctp_get(src, dst); if (!session) { error("Unable to create new AVCTP session"); - close(cli_sk); - return TRUE; + goto drop; } if (session->sock >= 0) { error("Refusing unexpected connect from %s", address); - close(cli_sk); - return TRUE; + goto drop; } session->state = AVCTP_STATE_CONNECTING; - session->mtu = l2o.imtu; - session->sock = cli_sk; + session->sock = g_io_channel_unix_get_fd(chan); - io = g_io_channel_unix_new(session->sock); - session->io = g_io_add_watch(io, flags, (GIOFunc) session_cb, session); - g_io_channel_unref(io); + memset(&l2o, 0, sizeof(l2o)); + size = sizeof(l2o); + if (getsockopt(session->sock, SOL_L2CAP, L2CAP_OPTIONS, &l2o, &size) < 0) { + err = errno; + error("getsockopt(L2CAP_OPTIONS): %s (%d)", strerror(err), + err); + avctp_unref(session); + goto drop; + } - if (avdtp_is_connected(&src, &dst)) + session->mtu = l2o.imtu; + session->io = g_io_add_watch(chan, flags, (GIOFunc) session_cb, + session); + g_io_channel_unref(chan); + + if (avdtp_is_connected(src, dst)) goto proceed; - if (service_req_auth(&src, &dst, AVRCP_TARGET_UUID, auth_cb, session) == 0) + if (service_req_auth(src, dst, AVRCP_TARGET_UUID, auth_cb, session) == 0) goto proceed; - else if (!manager_authorize(&dst, AVRCP_TARGET_UUID, auth_cb_old, session, + else if (!manager_authorize(dst, AVRCP_TARGET_UUID, auth_cb_old, session, &session->pending_auth)) { - close(cli_sk); avctp_unref(session); - return TRUE; + goto drop; } proceed: if (!session->pending_auth) { session->state = AVCTP_STATE_CONNECTED; - session->dev = manager_device_connected(&dst, + session->dev = manager_device_connected(dst, AVRCP_TARGET_UUID); session->dev->control->session = session; init_uinput(session); @@ -813,7 +731,31 @@ proceed: DBUS_TYPE_INVALID); } - return TRUE; + return; + +drop: + g_io_channel_close(chan); + g_io_channel_unref(chan); +} + +static GIOChannel *avctp_server_socket(gboolean master) +{ + int lm; + GIOChannel *io; + + lm = L2CAP_LM_SECURE; + + if (master) + lm |= L2CAP_LM_MASTER; + + io = bt_l2cap_listen(BDADDR_ANY, AVCTP_PSM, 0, lm, avctp_server_cb, + NULL); + if (!io) { + error("Unable to allocate new io channel"); + return NULL; + } + + return io; } static void avctp_connect_cb(GIOChannel *chan, int err, const bdaddr_t *src, @@ -856,7 +798,6 @@ static void avctp_connect_cb(GIOChannel *chan, int err, const bdaddr_t *src, DBUS_TYPE_INVALID); session->state = AVCTP_STATE_CONNECTED; - session->mtu = l2o.imtu; session->io = g_io_add_watch(chan, G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL, @@ -957,9 +898,6 @@ int avrcp_init(DBusConnection *conn, GKeyFile *config) if (!avctp_server) return -1; - g_io_add_watch(avctp_server, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, - (GIOFunc) avctp_server_cb, NULL); - return 0; } -- cgit From caf9fdd6d0183afc3b21d1cd82eb637773c131de Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Thu, 29 May 2008 08:05:16 +0000 Subject: Replace struct device with struct audio_device --- audio/control.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'audio/control.c') diff --git a/audio/control.c b/audio/control.c index 9dfcd46d..b10fb1dd 100644 --- a/audio/control.c +++ b/audio/control.c @@ -151,7 +151,7 @@ struct avrcp_header { #endif struct avctp { - struct device *dev; + struct audio_device *dev; avctp_state_t state; @@ -804,7 +804,7 @@ static void avctp_connect_cb(GIOChannel *chan, int err, const bdaddr_t *src, (GIOFunc) session_cb, session); } -gboolean avrcp_connect(struct device *dev) +gboolean avrcp_connect(struct audio_device *dev) { struct control *control = dev->control; struct avctp *session; @@ -835,7 +835,7 @@ gboolean avrcp_connect(struct device *dev) return TRUE; } -void avrcp_disconnect(struct device *dev) +void avrcp_disconnect(struct audio_device *dev) { struct control *control = dev->control; struct avctp *session = control->session; @@ -924,7 +924,7 @@ static DBusHandlerResult control_is_connected(DBusConnection *conn, DBusMessage *msg, void *data) { - struct device *device = data; + struct audio_device *device = data; struct control *control = device->control; DBusMessage *reply; dbus_bool_t connected; @@ -954,7 +954,7 @@ static DBusSignalVTable control_signals[] = { { NULL, NULL } }; -struct control *control_init(struct device *dev) +struct control *control_init(struct audio_device *dev) { if (!dbus_connection_register_interface(dev->conn, dev->path, AUDIO_CONTROL_INTERFACE, @@ -965,7 +965,7 @@ struct control *control_init(struct device *dev) return g_new0(struct control, 1); } -void control_free(struct device *dev) +void control_free(struct audio_device *dev) { struct control *control = dev->control; @@ -976,7 +976,7 @@ void control_free(struct device *dev) dev->control = NULL; } -gboolean control_is_active(struct device *dev) +gboolean control_is_active(struct audio_device *dev) { struct control *control = dev->control; -- cgit From b6029c0ffd7facd5f41bd665c84043d08961847d Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Mon, 2 Jun 2008 14:34:58 +0000 Subject: Make audio service to use libgdbus functions. --- audio/control.c | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) (limited to 'audio/control.c') diff --git a/audio/control.c b/audio/control.c index b10fb1dd..c9663a26 100644 --- a/audio/control.c +++ b/audio/control.c @@ -920,7 +920,7 @@ void avrcp_exit(void) connection = NULL; } -static DBusHandlerResult control_is_connected(DBusConnection *conn, +static DBusMessage *control_is_connected(DBusConnection *conn, DBusMessage *msg, void *data) { @@ -931,24 +931,22 @@ static DBusHandlerResult control_is_connected(DBusConnection *conn, reply = dbus_message_new_method_return(msg); if (!reply) - return DBUS_HANDLER_RESULT_NEED_MEMORY; + return NULL; connected = (control->session != NULL); dbus_message_append_args(reply, DBUS_TYPE_BOOLEAN, &connected, DBUS_TYPE_INVALID); - send_message_and_unref(conn, reply); - - return DBUS_HANDLER_RESULT_HANDLED; + return reply; } -static DBusMethodVTable control_methods[] = { - { "IsConnected", control_is_connected, "", "b" }, +static GDBusMethodTable control_methods[] = { + { "IsConnected", "", "b", control_is_connected }, { NULL, NULL, NULL, NULL } }; -static DBusSignalVTable control_signals[] = { +static GDBusSignalTable control_signals[] = { { "Connected", "" }, { "Disconnected", "" }, { NULL, NULL } @@ -956,10 +954,10 @@ static DBusSignalVTable control_signals[] = { struct control *control_init(struct audio_device *dev) { - if (!dbus_connection_register_interface(dev->conn, dev->path, - AUDIO_CONTROL_INTERFACE, - control_methods, - control_signals, NULL)) + if (!g_dbus_register_interface(dev->conn, dev->path, + AUDIO_CONTROL_INTERFACE, + control_methods, control_signals, NULL, + dev, NULL)) return NULL; return g_new0(struct control, 1); -- cgit From f80a7215275b229a597cf8d2bbc7e4e208af522c Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sat, 7 Jun 2008 19:30:24 +0000 Subject: Use g_dbus_emit_signal for sending D-Bus signals --- audio/control.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'audio/control.c') diff --git a/audio/control.c b/audio/control.c index c9663a26..6778b268 100644 --- a/audio/control.c +++ b/audio/control.c @@ -428,7 +428,7 @@ static void avctp_unref(struct avctp *session) } if (session->state == AVCTP_STATE_CONNECTED) - dbus_connection_emit_signal(session->dev->conn, + g_dbus_emit_signal(session->dev->conn, session->dev->path, AUDIO_CONTROL_INTERFACE, "Disconnected", @@ -635,7 +635,7 @@ static void auth_cb(DBusError *derr, void *user_data) session->dev->control->session = session; init_uinput(session); - dbus_connection_emit_signal(session->dev->conn, session->dev->path, + g_dbus_emit_signal(session->dev->conn, session->dev->path, AUDIO_CONTROL_INTERFACE, "Connected", DBUS_TYPE_INVALID); @@ -724,7 +724,7 @@ proceed: session->dev->control->session = session; init_uinput(session); flags |= G_IO_IN; - dbus_connection_emit_signal(session->dev->conn, + g_dbus_emit_signal(session->dev->conn, session->dev->path, AUDIO_CONTROL_INTERFACE, "Connected", @@ -793,7 +793,7 @@ static void avctp_connect_cb(GIOChannel *chan, int err, const bdaddr_t *src, init_uinput(session); - dbus_connection_emit_signal(session->dev->conn, session->dev->path, + g_dbus_emit_signal(session->dev->conn, session->dev->path, AUDIO_CONTROL_INTERFACE, "Connected", DBUS_TYPE_INVALID); -- cgit From e8ca2351ee3ba3f8b2b99731972234f42ae9b64b Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Tue, 17 Jun 2008 19:37:36 +0000 Subject: Fix authorization mechanism for 3.x. --- audio/control.c | 91 +++++++++++++++++---------------------------------------- 1 file changed, 27 insertions(+), 64 deletions(-) (limited to 'audio/control.c') diff --git a/audio/control.c b/audio/control.c index 6778b268..9139213b 100644 --- a/audio/control.c +++ b/audio/control.c @@ -165,8 +165,6 @@ struct avctp { guint io; uint16_t mtu; - - DBusPendingCall *pending_auth; }; struct control { @@ -323,12 +321,8 @@ static struct avctp *avctp_get(const bdaddr_t *src, const bdaddr_t *dst) assert(dst != NULL); session = find_session(src, dst); - if (session) { - if (session->pending_auth) - return NULL; - else - return session; - } + if (session) + return session; session = g_new0(struct avctp, 1); @@ -420,13 +414,6 @@ static void avctp_unref(struct avctp *session) { sessions = g_slist_remove(sessions, session); - if (session->pending_auth) { - manager_cancel_authorize(&session->dst, AVRCP_TARGET_UUID, - NULL); - dbus_pending_call_cancel(session->pending_auth); - dbus_pending_call_unref(session->pending_auth); - } - if (session->state == AVCTP_STATE_CONNECTED) g_dbus_emit_signal(session->dev->conn, session->dev->path, @@ -609,56 +596,47 @@ static void init_uinput(struct avctp *session) debug("AVRCP: uinput initialized for %s", name); } -static void auth_cb(DBusError *derr, void *user_data) +static void avctp_connect_session(struct avctp *session) { - struct avctp *session = user_data; GIOChannel *io; - if (derr && dbus_error_is_set(derr)) { - error("Access denied: %s", derr->message); - if (dbus_error_has_name(derr, DBUS_ERROR_NO_REPLY)) { - debug("Canceling authorization request"); - if (service_cancel_auth(&session->dst) < 0) - manager_cancel_authorize(&session->dst, - AVRCP_TARGET_UUID, - NULL); - } - - avctp_unref(session); - return; - } - session->state = AVCTP_STATE_CONNECTED; - session->dev = manager_device_connected(&session->dst, AVRCP_TARGET_UUID); session->dev->control->session = session; + init_uinput(session); g_dbus_emit_signal(session->dev->conn, session->dev->path, AUDIO_CONTROL_INTERFACE, "Connected", DBUS_TYPE_INVALID); - g_source_remove(session->io); + if (session->io) + g_source_remove(session->io); io = g_io_channel_unix_new(session->sock); session->io = g_io_add_watch(io, - G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL, - (GIOFunc) session_cb, session); + G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL, + (GIOFunc) session_cb, session); g_io_channel_unref(io); } -static void auth_cb_old(DBusPendingCall *call, void *data) +static void auth_cb(DBusError *derr, void *user_data) { - DBusMessage *reply = dbus_pending_call_steal_reply(call); - DBusError err; + struct avctp *session = user_data; - dbus_error_init(&err); - dbus_set_error_from_message(&err, reply); - auth_cb(&err, data); - dbus_error_free(&err); + if (derr && dbus_error_is_set(derr)) { + error("Access denied: %s", derr->message); + if (dbus_error_has_name(derr, DBUS_ERROR_NO_REPLY)) { + debug("Canceling authorization request"); + service_cancel_auth(&session->src, &session->dst); + } - dbus_message_unref(reply); + avctp_unref(session); + return; + } + + avctp_connect_session(session); } static void avctp_server_cb(GIOChannel *chan, int err, const bdaddr_t *src, @@ -701,6 +679,7 @@ static void avctp_server_cb(GIOChannel *chan, int err, const bdaddr_t *src, } session->mtu = l2o.imtu; + session->io = g_io_add_watch(chan, flags, (GIOFunc) session_cb, session); g_io_channel_unref(chan); @@ -708,34 +687,18 @@ static void avctp_server_cb(GIOChannel *chan, int err, const bdaddr_t *src, if (avdtp_is_connected(src, dst)) goto proceed; - if (service_req_auth(src, dst, AVRCP_TARGET_UUID, auth_cb, session) == 0) - goto proceed; - else if (!manager_authorize(dst, AVRCP_TARGET_UUID, auth_cb_old, session, - &session->pending_auth)) { - avctp_unref(session); + if (service_req_auth(src, dst, AVRCP_TARGET_UUID, auth_cb, session) < 0) goto drop; - } + + return; proceed: - if (!session->pending_auth) { - session->state = AVCTP_STATE_CONNECTED; - session->dev = manager_device_connected(dst, - AVRCP_TARGET_UUID); - session->dev->control->session = session; - init_uinput(session); - flags |= G_IO_IN; - g_dbus_emit_signal(session->dev->conn, - session->dev->path, - AUDIO_CONTROL_INTERFACE, - "Connected", - DBUS_TYPE_INVALID); - } + avctp_connect_session(session); return; drop: - g_io_channel_close(chan); - g_io_channel_unref(chan); + close(session->sock); } static GIOChannel *avctp_server_socket(gboolean master) -- cgit